From 931c6efb1aa807d54fae26d5603359c2489799d4 Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 5 Sep 2013 19:14:22 +0300 Subject: [PATCH] converted minutebucket to balance, compiled but test fail --- engine/action.go | 39 ++--- engine/action_timing.go | 5 +- engine/action_trigger.go | 12 +- engine/actions_test.go | 122 ++++++-------- engine/balances.go | 17 +- engine/calldesc.go | 10 +- engine/calldesc_test.go | 21 ++- engine/loader_csv.go | 73 ++++---- engine/minute_buckets.go | 90 ---------- engine/minute_buckets_test.go | 28 ++-- engine/storage_sql.go | 43 ++--- engine/storage_test.go | 11 +- engine/timespans.go | 14 +- engine/timespans_test.go | 46 ++--- engine/units_counter.go | 24 +-- engine/units_counter_test.go | 24 +-- engine/userbalance.go | 101 +++++------ engine/userbalance_test.go | 308 +++++++++++++++++----------------- 18 files changed, 419 insertions(+), 569 deletions(-) delete mode 100644 engine/minute_buckets.go diff --git a/engine/action.go b/engine/action.go index c667326ac..1dcf33baf 100644 --- a/engine/action.go +++ b/engine/action.go @@ -28,17 +28,16 @@ import ( Structure to be filled for each tariff plan with the bonus value for received calls minutes. */ type Action struct { - Id string - ActionType string - BalanceId string - Direction string - ExpirationString string - ExpirationDate time.Time - Units float64 - Weight float64 - MinuteBucket *MinuteBucket - DestinationTag, RateType string // From here for import/load purposes only - RateValue, MinutesWeight float64 + Id string + ActionType string + BalanceId string + Direction string + ExpirationString string + Weight float64 + Balance *Balance + DestinationTag, RateType string // From here for import/load purposes only + ExpirationDate time.Time + Units, RateValue, MinutesWeight float64 } const ( @@ -86,7 +85,7 @@ func getActionFunc(typ string) (actionTypeFunc, bool) { } func logAction(ub *UserBalance, a *Action) (err error) { - Logger.Info(fmt.Sprintf("%v %v %v", a.BalanceId, a.Units, a.MinuteBucket)) + Logger.Info(fmt.Sprintf("%v %v %v", a.BalanceId, a.Balance)) return } @@ -116,11 +115,7 @@ func resetPrepaidAction(ub *UserBalance, a *Action) (err error) { } func topupResetAction(ub *UserBalance, a *Action) (err error) { - if a.BalanceId == MINUTES { - ub.MinuteBuckets = make([]*MinuteBucket, 0) - } else { - ub.BalanceMap[a.BalanceId+a.Direction] = BalanceChain{&Balance{Value: 0}} // ToDo: can ub be empty here? - } + ub.BalanceMap[a.BalanceId+a.Direction] = BalanceChain{&Balance{Value: 0}} // ToDo: can ub be empty here? genericMakeNegative(a) genericDebit(ub, a) return @@ -157,11 +152,8 @@ func resetCountersAction(ub *UserBalance, a *Action) (err error) { } func genericMakeNegative(a *Action) { - if a.Units > 0 { // only apply if not allready negative - a.Units = -a.Units - } - if a.MinuteBucket != nil && a.MinuteBucket.Seconds > 0 { - a.MinuteBucket.Seconds = -a.MinuteBucket.Seconds + if a.Balance != nil && a.Balance.Value > 0 { // only apply if not allready negative + a.Balance.Value = -a.Balance.Value } } @@ -170,7 +162,7 @@ func genericDebit(ub *UserBalance, a *Action) (err error) { ub.BalanceMap = make(map[string]BalanceChain) } if a.BalanceId == MINUTES { - ub.debitMinuteBucket(a.MinuteBucket) + ub.debitMinuteBalance(a.Balance) } else { ub.debitBalanceAction(a) } @@ -181,7 +173,6 @@ func genericReset(ub *UserBalance) { for k, _ := range ub.BalanceMap { ub.BalanceMap[k] = BalanceChain{&Balance{Value: 0}} } - ub.MinuteBuckets = make([]*MinuteBucket, 0) ub.UnitCounters = make([]*UnitsCounter, 0) ub.resetActionTriggers(nil) } diff --git a/engine/action_timing.go b/engine/action_timing.go index c2ec2d8db..a608b9dcb 100644 --- a/engine/action_timing.go +++ b/engine/action_timing.go @@ -219,10 +219,7 @@ func (at *ActionTiming) Execute() (err error) { return } for _, a := range aac { - a.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) - if a.MinuteBucket != nil { - a.MinuteBucket.ExpirationDate = a.ExpirationDate - } + a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) actionFunction, exists := getActionFunc(a.ActionType) if !exists { Logger.Crit(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) diff --git a/engine/action_trigger.go b/engine/action_trigger.go index 3a8aaa281..2b1c7e2a7 100644 --- a/engine/action_trigger.go +++ b/engine/action_trigger.go @@ -46,10 +46,10 @@ func (at *ActionTrigger) Execute(ub *UserBalance) (err error) { return } for _, a := range aac { - a.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) - if a.MinuteBucket != nil { - a.MinuteBucket.ExpirationDate = a.ExpirationDate + if a.Balance == nil { + a.Balance = &Balance{} } + a.Balance.ExpirationDate, _ = utils.ParseDate(a.ExpirationString) actionFunction, exists := getActionFunc(a.ActionType) if !exists { Logger.Warning(fmt.Sprintf("Function type %v not available, aborting execution!", a.ActionType)) @@ -73,9 +73,9 @@ func (at *ActionTrigger) Match(a *Action) bool { id := a.BalanceId == "" || at.BalanceId == a.BalanceId direction := a.Direction == "" || at.Direction == a.Direction thresholdType, thresholdValue := true, true - if a.MinuteBucket != nil { - thresholdType = a.MinuteBucket.PriceType == "" || at.ThresholdType == a.MinuteBucket.PriceType - thresholdValue = a.MinuteBucket.Price == 0 || at.ThresholdValue == a.MinuteBucket.Price + if a.Balance != nil { + thresholdType = a.Balance.SpecialPriceType == "" || at.ThresholdType == a.Balance.SpecialPriceType + thresholdValue = a.Balance.SpecialPrice == 0 || at.ThresholdValue == a.Balance.SpecialPrice } return id && direction && thresholdType && thresholdValue } diff --git a/engine/actions_test.go b/engine/actions_test.go index e735bc4e2..47d27bae2 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -338,10 +338,9 @@ func TestActionTimingOneTimeRun(t *testing.T) { func TestActionTimingLogFunction(t *testing.T) { a := &Action{ - ActionType: "*log", - BalanceId: "test", - Units: 1.1, - MinuteBucket: &MinuteBucket{}, + ActionType: "*log", + BalanceId: "test", + Balance: &Balance{Value: 1.1}, } at := &ActionTiming{ actions: []*Action{a}, @@ -446,7 +445,7 @@ func TestActionTriggerMatchMinuteBucketFull(t *testing.T) { ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 2}} + a := &Action{Balance: &Balance{SpecialPriceType: TRIGGER_MAX_BALANCE, SpecialPrice: 2}} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -459,7 +458,7 @@ func TestActionTriggerMatchAllFull(t *testing.T) { ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 2}} + a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, Balance: &Balance{SpecialPriceType: TRIGGER_MAX_BALANCE, SpecialPrice: 2}} if !at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -472,20 +471,20 @@ func TestActionTriggerMatchSomeFalse(t *testing.T) { ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: INBOUND, BalanceId: CREDIT, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 2}} + a := &Action{Direction: INBOUND, BalanceId: CREDIT, Balance: &Balance{SpecialPriceType: TRIGGER_MAX_BALANCE, SpecialPrice: 2}} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } } -func TestActionTriggerMatcMinuteBucketFalse(t *testing.T) { +func TestActionTriggerMatcBalanceFalse(t *testing.T) { at := &ActionTrigger{ Direction: OUTBOUND, BalanceId: CREDIT, ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_BALANCE, Price: 3}} + a := &Action{Direction: OUTBOUND, BalanceId: CREDIT, Balance: &Balance{SpecialPriceType: TRIGGER_MAX_BALANCE, SpecialPrice: 3}} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -498,7 +497,7 @@ func TestActionTriggerMatcAllFalse(t *testing.T) { ThresholdType: TRIGGER_MAX_BALANCE, ThresholdValue: 2, } - a := &Action{Direction: INBOUND, BalanceId: MINUTES, MinuteBucket: &MinuteBucket{PriceType: TRIGGER_MAX_COUNTER, Price: 3}} + a := &Action{Direction: INBOUND, BalanceId: MINUTES, Balance: &Balance{SpecialPriceType: TRIGGER_MAX_COUNTER, SpecialPrice: 3}} if at.Match(a) { t.Errorf("Action trigger [%v] does not match action [%v]", at, a) } @@ -522,7 +521,7 @@ func TestActionTriggerPriotityList(t *testing.T) { BalanceId: "BALANCE", Units: 10, Weight: 11, - MinuteBucket: &MinuteBucket{}, + Balance: &Balance{}, } logAction(nil, a) }*/ @@ -530,9 +529,8 @@ func TestActionTriggerPriotityList(t *testing.T) { func TestActionResetTriggres(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, nil) @@ -557,9 +555,8 @@ func TestActionResetTriggresExecutesThem(t *testing.T) { func TestActionResetTriggresActionFilter(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 10}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetTriggersAction(ub, &Action{BalanceId: SMS}) @@ -572,9 +569,8 @@ func TestActionSetPostpaid(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } setPostpaidAction(ub, nil) @@ -587,9 +583,8 @@ func TestActionSetPrepaid(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } setPrepaidAction(ub, nil) @@ -602,16 +597,15 @@ func TestActionResetPrepaid(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetPrepaidAction(ub, nil) if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 0 || len(ub.UnitCounters) != 0 || - len(ub.MinuteBuckets) != 0 || + len(ub.BalanceMap[MINUTES]) != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { t.Error("Reset prepaid action failed!") } @@ -621,16 +615,15 @@ func TestActionResetPostpaid(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: SMS, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetPostpaidAction(ub, nil) if ub.Type != UB_TYPE_POSTPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 0 || len(ub.UnitCounters) != 0 || - len(ub.MinuteBuckets) != 0 || + len(ub.BalanceMap[MINUTES]) != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { t.Error("Reset postpaid action failed!") } @@ -640,9 +633,8 @@ func TestActionTopupResetCredit(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10} @@ -650,7 +642,7 @@ func TestActionTopupResetCredit(t *testing.T) { if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 10 || len(ub.UnitCounters) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Errorf("Topup reset action failed: %#v", ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue()) } @@ -660,20 +652,19 @@ func TestActionTopupResetMinutes(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationId: "NAT"}} + a := &Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: 5, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}} topupResetAction(ub, a) if ub.Type != UB_TYPE_PREPAID || - ub.MinuteBuckets[0].Seconds != 5 || + ub.BalanceMap[MINUTES][0].Value != 5 || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.MinuteBuckets) != 1 || + len(ub.BalanceMap[MINUTES]) != 1 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Error("Topup reset minutes action failed!", ub.MinuteBuckets[0]) + t.Error("Topup reset minutes action failed!", ub.BalanceMap[MINUTES][0]) } } @@ -681,9 +672,8 @@ func TestActionTopupCredit(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10} @@ -691,7 +681,7 @@ func TestActionTopupCredit(t *testing.T) { if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 110 || len(ub.UnitCounters) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Topup action failed!", ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue()) } @@ -701,20 +691,19 @@ func TestActionTopupMinutes(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationId: "NAT"}} + a := &Action{BalanceId: MINUTES, Balance: &Balance{Value: 5, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}} topupAction(ub, a) if ub.Type != UB_TYPE_PREPAID || - ub.MinuteBuckets[0].Seconds != 15 || + ub.BalanceMap[MINUTES][0].Value != 15 || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Error("Topup minutes action failed!", ub.MinuteBuckets[0]) + t.Error("Topup minutes action failed!", ub.BalanceMap[MINUTES][0]) } } @@ -722,9 +711,8 @@ func TestActionDebitCredit(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10} @@ -732,7 +720,7 @@ func TestActionDebitCredit(t *testing.T) { if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 90 || len(ub.UnitCounters) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { t.Error("Debit action failed!", ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue()) } @@ -742,20 +730,19 @@ func TestActionDebitMinutes(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}, &ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } - a := &Action{BalanceId: MINUTES, MinuteBucket: &MinuteBucket{Seconds: 5, Weight: 20, Price: 1, DestinationId: "NAT"}} + a := &Action{BalanceId: MINUTES, Balance: &Balance{Value: 5, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}} debitAction(ub, a) if ub.Type != UB_TYPE_PREPAID || - ub.MinuteBuckets[0].Seconds != 5 || + ub.BalanceMap[MINUTES][0].Value != 5 || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Error("Debit minutes action failed!", ub.MinuteBuckets[0]) + t.Error("Debit minutes action failed!", ub.BalanceMap[MINUTES][0]) } } @@ -763,25 +750,24 @@ func TestActionResetAllCounters(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } resetCountersAction(ub, nil) if ub.Type != UB_TYPE_POSTPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 1 || - len(ub.UnitCounters[0].MinuteBuckets) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.UnitCounters[0].MinuteBalances) != 1 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true { t.Error("Reset counters action failed!") } if len(ub.UnitCounters) < 1 { t.FailNow() } - mb := ub.UnitCounters[0].MinuteBuckets[0] - if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationId != "NAT" { + mb := ub.UnitCounters[0].MinuteBalances[0] + if mb.Weight != 20 || mb.SpecialPrice != 1 || mb.Value != 10 || mb.DestinationId != "NAT" { t.Errorf("Minute bucked cloned incorrectly: %v!", mb) } } @@ -790,9 +776,8 @@ func TestActionResetCounterMinutes(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: MINUTES} @@ -800,16 +785,16 @@ func TestActionResetCounterMinutes(t *testing.T) { if ub.Type != UB_TYPE_POSTPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 2 || - len(ub.UnitCounters[1].MinuteBuckets) != 1 || - len(ub.MinuteBuckets) != 2 || + len(ub.UnitCounters[1].MinuteBalances) != 1 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true { - t.Error("Reset counters action failed!", ub.UnitCounters[1].MinuteBuckets) + t.Error("Reset counters action failed!", ub.UnitCounters[1].MinuteBalances) } - if len(ub.UnitCounters) < 2 || len(ub.UnitCounters[1].MinuteBuckets) < 1 { + if len(ub.UnitCounters) < 2 || len(ub.UnitCounters[1].MinuteBalances) < 1 { t.FailNow() } - mb := ub.UnitCounters[1].MinuteBuckets[0] - if mb.Weight != 20 || mb.Price != 1 || mb.Seconds != 10 || mb.DestinationId != "NAT" { + mb := ub.UnitCounters[1].MinuteBalances[0] + if mb.Weight != 20 || mb.SpecialPrice != 1 || mb.Value != 10 || mb.DestinationId != "NAT" { t.Errorf("Minute bucked cloned incorrectly: %v!", mb) } } @@ -818,9 +803,8 @@ func TestActionResetCounterCREDIT(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}, &UnitsCounter{BalanceId: SMS, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ActionsId: "TEST_ACTIONS", Executed: true}}, } a := &Action{BalanceId: CREDIT, Direction: OUTBOUND} @@ -828,7 +812,7 @@ func TestActionResetCounterCREDIT(t *testing.T) { if ub.Type != UB_TYPE_POSTPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 100 || len(ub.UnitCounters) != 2 || - len(ub.MinuteBuckets) != 2 || + len(ub.BalanceMap[MINUTES]) != 2 || ub.ActionTriggers[0].Executed != true { t.Error("Reset counters action failed!", ub.UnitCounters) } diff --git a/engine/balances.go b/engine/balances.go index 4f68fb8e7..036113c42 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -26,14 +26,15 @@ import ( // Can hold different units as seconds or monetary type Balance struct { - Id string - Value float64 - ExpirationDate time.Time - Weight float64 - GroupIds []string - SpecialPrice float64 // absolute for minutes and percent for monetary (can be positive or negative) - DestinationId string - precision int + Id string + Value float64 + ExpirationDate time.Time + Weight float64 + GroupIds []string + SpecialPriceType string + SpecialPrice float64 // absolute for minutes and percent for monetary (can be positive or negative) + DestinationId string + precision int } func (b *Balance) Equal(o *Balance) bool { diff --git a/engine/calldesc.go b/engine/calldesc.go index 88394e810..d72a97eb9 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -197,13 +197,13 @@ func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*Ti timespans = append(timespans, firstSpan) // split on (free) minute buckets if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { - _, _, bucketList := userBalance.getSecondsForPrefix(cd.Destination) - for _, mb := range bucketList { + _, _, minuteBalances := userBalance.getSecondsForPrefix(cd.Destination) + for _, b := range minuteBalances { for i := 0; i < len(timespans); i++ { if timespans[i].MinuteInfo != nil { continue } - newTs := timespans[i].SplitByMinuteBucket(mb) + newTs := timespans[i].SplitByMinuteBalance(b) if newTs != nil { timespans = append(timespans, newTs) firstSpan = newTs // we move the firstspan to the newly created one for further spliting @@ -443,8 +443,8 @@ The amount filed has to be filled in call descriptor. func (cd *CallDescriptor) AddRecievedCallSeconds() (err error) { if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil { a := &Action{ - Direction: INBOUND, - MinuteBucket: &MinuteBucket{Seconds: cd.Amount, DestinationId: cd.Destination}, + Direction: INBOUND, + Balance: &Balance{Value: cd.Amount, DestinationId: cd.Destination}, } userBalance.countUnits(a) return nil diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index 706bcd3c2..647dbd84b 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -36,21 +36,20 @@ func init() { func populateDB() { minu := &UserBalance{ - Id: "*out:vdf:minu", - Type: UB_TYPE_PREPAID, - BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 0}}}, - MinuteBuckets: []*MinuteBucket{ - &MinuteBucket{Seconds: 200, DestinationId: "NAT", Weight: 10}, - &MinuteBucket{Seconds: 100, DestinationId: "RET", Weight: 20}, - }, + Id: "*out:vdf:minu", + Type: UB_TYPE_PREPAID, + BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 0}}, MINUTES: BalanceChain{ + &Balance{Value: 200, DestinationId: "NAT", Weight: 10}, + &Balance{Value: 100, DestinationId: "RET", Weight: 20}, + }}, } broker := &UserBalance{ Id: "*out:vdf:broker", Type: UB_TYPE_PREPAID, - MinuteBuckets: []*MinuteBucket{ - &MinuteBucket{Seconds: 20, DestinationId: "NAT", Weight: 10, Price: 1}, - &MinuteBucket{Seconds: 100, DestinationId: "RET", Weight: 20}, - }, + BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{ + &Balance{Value: 20, DestinationId: "NAT", Weight: 10, SpecialPrice: 1}, + &Balance{Value: 100, DestinationId: "RET", Weight: 20}, + }}, } if storageGetter != nil { storageGetter.(Storage).Flush() diff --git a/engine/loader_csv.go b/engine/loader_csv.go index 657c96c47..12e9939f4 100644 --- a/engine/loader_csv.go +++ b/engine/loader_csv.go @@ -348,50 +348,35 @@ func (csvr *CSVReader) LoadActions() (err error) { if err != nil { return errors.New(fmt.Sprintf("Could not parse action units: %v", err)) } - var a *Action - if record[2] != MINUTES { - a = &Action{ - ActionType: record[1], - BalanceId: record[2], - Direction: record[3], - Units: units, - ExpirationString: record[5], - } - if _, err := utils.ParseDate(a.ExpirationString); err != nil { - return errors.New(fmt.Sprintf("Could not parse expiration time: %v", err)) - } - } else { - value, err := strconv.ParseFloat(record[8], 64) - if err != nil { - return errors.New(fmt.Sprintf("Could not parse action price: %v", err)) - } - minutesWeight, err := strconv.ParseFloat(record[9], 64) - if err != nil { - return errors.New(fmt.Sprintf("Could not parse action minutes weight: %v", err)) - } - weight, err := strconv.ParseFloat(record[9], 64) - if err != nil { - return errors.New(fmt.Sprintf("Could not parse action weight: %v", err)) - } - a = &Action{ - Id: utils.GenUUID(), - ActionType: record[1], - BalanceId: record[2], - Direction: record[3], - Weight: weight, - ExpirationString: record[5], - MinuteBucket: &MinuteBucket{ - Seconds: units, - Weight: minutesWeight, - Price: value, - PriceType: record[7], - DestinationId: record[6], - }, - } - if _, err := utils.ParseDate(a.ExpirationString); err != nil { - return errors.New(fmt.Sprintf("Could not parse expiration time: %v", err)) - } - + value, err := strconv.ParseFloat(record[8], 64) + if err != nil { + return errors.New(fmt.Sprintf("Could not parse action price: %v", err)) + } + minutesWeight, err := strconv.ParseFloat(record[9], 64) + if err != nil { + return errors.New(fmt.Sprintf("Could not parse action minutes weight: %v", err)) + } + weight, err := strconv.ParseFloat(record[9], 64) + if err != nil { + return errors.New(fmt.Sprintf("Could not parse action weight: %v", err)) + } + a := &Action{ + Id: utils.GenUUID(), + ActionType: record[1], + BalanceId: record[2], + Direction: record[3], + Weight: weight, + ExpirationString: record[5], + Balance: &Balance{ + Value: units, + Weight: minutesWeight, + SpecialPrice: value, + SpecialPriceType: record[7], + DestinationId: record[6], + }, + } + if _, err := utils.ParseDate(a.ExpirationString); err != nil { + return errors.New(fmt.Sprintf("Could not parse expiration time: %v", err)) } csvr.actions[tag] = append(csvr.actions[tag], a) } diff --git a/engine/minute_buckets.go b/engine/minute_buckets.go deleted file mode 100644 index ca6a994b2..000000000 --- a/engine/minute_buckets.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Rating system designed to be used in VoIP Carriers World -Copyright (C) 2013 ITsysCOM - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see -*/ - -package engine - -import ( - "math" - "sort" - "time" -) - -type MinuteBucket struct { - Seconds float64 - Weight float64 - Price float64 // percentage from standard price or absolute value depending on Type - PriceType string - DestinationId string - ExpirationDate time.Time - precision int -} - -// Returns the available number of seconds for a specified credit -func (mb *MinuteBucket) GetSecondsForCredit(credit float64) (seconds float64) { - seconds = mb.Seconds - if mb.Price > 0 { - seconds = math.Min(credit/mb.Price, mb.Seconds) - } - return -} - -// Creates a similar minute -func (mb *MinuteBucket) Clone() *MinuteBucket { - return &MinuteBucket{ - Seconds: mb.Seconds, - Weight: mb.Weight, - Price: mb.Price, - PriceType: mb.PriceType, - DestinationId: mb.DestinationId, - } -} - -// Equal method -func (mb *MinuteBucket) Equal(o *MinuteBucket) bool { - return mb.DestinationId == o.DestinationId && - mb.Weight == o.Weight && - mb.Price == o.Price && - mb.ExpirationDate.Equal(o.ExpirationDate) -} - -func (mb *MinuteBucket) IsExpired() bool { - return !mb.ExpirationDate.IsZero() && mb.ExpirationDate.Before(time.Now()) -} - -/* -Structure to store minute buckets according to weight, precision or price. -*/ -type bucketsorter []*MinuteBucket - -func (bs bucketsorter) Len() int { - return len(bs) -} - -func (bs bucketsorter) Swap(i, j int) { - bs[i], bs[j] = bs[j], bs[i] -} - -func (bs bucketsorter) Less(j, i int) bool { - return bs[i].Weight < bs[j].Weight || - bs[i].precision < bs[j].precision || - bs[i].Price > bs[j].Price -} - -func (bs bucketsorter) Sort() { - sort.Sort(bs) -} diff --git a/engine/minute_buckets_test.go b/engine/minute_buckets_test.go index acdf5972e..b218d2334 100644 --- a/engine/minute_buckets_test.go +++ b/engine/minute_buckets_test.go @@ -24,9 +24,9 @@ import ( ) func TestMinutBucketSortWeight(t *testing.T) { - mb1 := &MinuteBucket{Weight: 1, precision: 2, Price: 2} - mb2 := &MinuteBucket{Weight: 2, precision: 1, Price: 1} - var bs bucketsorter + mb1 := &Balance{Weight: 1, precision: 2, SpecialPrice: 2} + mb2 := &Balance{Weight: 2, precision: 1, SpecialPrice: 1} + var bs BalanceChain bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -35,9 +35,9 @@ func TestMinutBucketSortWeight(t *testing.T) { } func TestMinutBucketSortPrecision(t *testing.T) { - mb1 := &MinuteBucket{Weight: 1, precision: 2, Price: 2} - mb2 := &MinuteBucket{Weight: 1, precision: 1, Price: 1} - var bs bucketsorter + mb1 := &Balance{Weight: 1, precision: 2, SpecialPrice: 2} + mb2 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1} + var bs BalanceChain bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -45,10 +45,10 @@ func TestMinutBucketSortPrecision(t *testing.T) { } } -func TestMinutBucketSortPrice(t *testing.T) { - mb1 := &MinuteBucket{Weight: 1, precision: 1, Price: 1} - mb2 := &MinuteBucket{Weight: 1, precision: 1, Price: 2} - var bs bucketsorter +func TestMinutBucketSortSpecialPrice(t *testing.T) { + mb1 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1} + mb2 := &Balance{Weight: 1, precision: 1, SpecialPrice: 2} + var bs BalanceChain bs = append(bs, mb2, mb1) bs.Sort() if bs[0] != mb1 || bs[1] != mb2 { @@ -57,16 +57,16 @@ func TestMinutBucketSortPrice(t *testing.T) { } func TestMinutBucketEqual(t *testing.T) { - mb1 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, PriceType: PRICE_ABSOLUTE, DestinationId: ""} - mb2 := &MinuteBucket{Weight: 1, precision: 1, Price: 1, PriceType: PRICE_ABSOLUTE, DestinationId: ""} - mb3 := &MinuteBucket{Weight: 1, precision: 1, Price: 2, PriceType: PRICE_ABSOLUTE, DestinationId: ""} + mb1 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: ""} + mb2 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: ""} + mb3 := &Balance{Weight: 1, precision: 1, SpecialPrice: 2, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: ""} if !mb1.Equal(mb2) || mb2.Equal(mb3) { t.Error("Equal failure!", mb1, mb2, mb3) } } func TestMinutBucketClone(t *testing.T) { - mb1 := &MinuteBucket{Seconds: 1, Weight: 2, Price: 3, PriceType: PRICE_ABSOLUTE, DestinationId: "5"} + mb1 := &Balance{Value: 1, Weight: 2, SpecialPrice: 3, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "5"} mb2 := mb1.Clone() if mb1 == mb2 || !reflect.DeepEqual(mb1, mb2) { t.Error("Cloning failure: ", mb1, mb2) diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 7c83e7756..2668849d8 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -540,7 +540,7 @@ func (self *SQLStorage) SetTPActions(tpid string, acts map[string][]*Action) err qry += "," } qry += fmt.Sprintf("('%s','%s','%s','%s','%s',%f,'%s','%s','%s',%f,%f,%f)", - tpid, actId, act.ActionType, act.BalanceId, act.Direction, act.Units, act.ExpirationString, + tpid, actId, act.ActionType, act.BalanceId, act.Direction, act.Balance.Value, act.ExpirationString, act.DestinationTag, act.RateType, act.RateValue, act.MinutesWeight, act.Weight) i++ } @@ -1083,32 +1083,21 @@ func (self *SQLStorage) GetTpActions(tpid, tag string) (map[string][]*Action, er if err := rows.Scan(&id, &tpid, &tag, &action, &balance_type, &direction, &units, &expirationDate, &destinations_tag, &rate_type, &rate, &minutes_weight, &weight); err != nil { return nil, err } - var a *Action - if balance_type != MINUTES { - a = &Action{ - ActionType: action, - BalanceId: balance_type, - Direction: direction, - Units: units, - ExpirationString: expirationDate, - } - } else { - var price float64 - a = &Action{ - Id: utils.GenUUID(), - ActionType: action, - BalanceId: balance_type, - Direction: direction, - Weight: weight, - ExpirationString: expirationDate, - MinuteBucket: &MinuteBucket{ - Seconds: units, - Weight: minutes_weight, - Price: price, - PriceType: rate_type, - DestinationId: destinations_tag, - }, - } + var price float64 + a := &Action{ + Id: utils.GenUUID(), + ActionType: action, + BalanceId: balance_type, + Direction: direction, + Weight: weight, + ExpirationString: expirationDate, + Balance: &Balance{ + Value: units, + Weight: minutes_weight, + SpecialPrice: price, + SpecialPriceType: rate_type, + DestinationId: destinations_tag, + }, } as[tag] = append(as[tag], a) } diff --git a/engine/storage_test.go b/engine/storage_test.go index d13865c03..e77d99bf5 100644 --- a/engine/storage_test.go +++ b/engine/storage_test.go @@ -75,10 +75,10 @@ func TestMsgpackTime(t *testing.T) { func GetUB() *UserBalance { uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + Direction: OUTBOUND, + BalanceId: SMS, + Units: 100, + MinuteBalances: BalanceChain{&Balance{Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, } at := &ActionTrigger{ Id: "some_uuid", @@ -94,8 +94,7 @@ func GetUB() *UserBalance { ub := &UserBalance{ Id: "rif", Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14, ExpirationDate: zeroTime}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024, ExpirationDate: zeroTime}}, MINUTES: BalanceChain{&Balance{Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{uc, uc}, ActionTriggers: ActionTriggerPriotityList{at, at, at}, } diff --git a/engine/timespans.go b/engine/timespans.go index 2b1134f5d..3a9da6d6a 100644 --- a/engine/timespans.go +++ b/engine/timespans.go @@ -181,7 +181,7 @@ func (ts *TimeSpan) SplitByActivationPeriod(ap *ActivationPeriod) (newTs *TimeSp /* Splits the given timespan on minute bucket's duration. */ -func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) { +func (ts *TimeSpan) SplitByMinuteBalance(mb *Balance) (newTs *TimeSpan) { // if mb expired skip it if !mb.ExpirationDate.IsZero() && (ts.TimeStart.Equal(mb.ExpirationDate) || ts.TimeStart.After(mb.ExpirationDate)) { return nil @@ -197,20 +197,20 @@ func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) { } s := ts.GetDuration().Seconds() - ts.MinuteInfo = &MinuteInfo{mb.DestinationId, s, mb.Price} - if s <= mb.Seconds { - mb.Seconds -= s + ts.MinuteInfo = &MinuteInfo{mb.DestinationId, s, mb.SpecialPrice} + if s <= mb.Value { + mb.Value -= s return newTs } - secDuration, _ := time.ParseDuration(fmt.Sprintf("%vs", mb.Seconds)) + secDuration, _ := time.ParseDuration(fmt.Sprintf("%vs", mb.Value)) newTimeEnd := ts.TimeStart.Add(secDuration) newTs = &TimeSpan{TimeStart: newTimeEnd, TimeEnd: ts.TimeEnd} ts.TimeEnd = newTimeEnd newTs.CallDuration = ts.CallDuration - ts.MinuteInfo.Quantity = mb.Seconds + ts.MinuteInfo.Quantity = mb.Value ts.SetNewCallDuration(newTs) - mb.Seconds = 0 + mb.Value = 0 return } diff --git a/engine/timespans_test.go b/engine/timespans_test.go index 8ecc3f7e0..6e1f54d8a 100644 --- a/engine/timespans_test.go +++ b/engine/timespans_test.go @@ -220,9 +220,9 @@ func TestSetInterval(t *testing.T) { func TestTimespanSplitByMinuteBucketPlenty(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 180} + mb := &Balance{Value: 180} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 120 { t.Error("Not enough minutes on minute bucket split") } @@ -231,12 +231,12 @@ func TestTimespanSplitByMinuteBucketPlenty(t *testing.T) { } } -func TestTimespanSplitByMinuteBucketScarce(t *testing.T) { +func TestTimespanSplitByMinuteBalanceScarce(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 60} + mb := &Balance{Value: 60} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 60 { t.Error("Not enough minutes on minute bucket split") } @@ -245,12 +245,12 @@ func TestTimespanSplitByMinuteBucketScarce(t *testing.T) { } } -func TestTimespanSplitByMinuteBucketPlentyExpired(t *testing.T) { +func TestTimespanSplitByMinuteBalancePlentyExpired(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 39, 0, 0, time.UTC)} + mb := &Balance{Value: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 39, 0, 0, time.UTC)} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo != nil { t.Error("Not enough minutes on minute bucket split") } @@ -259,12 +259,12 @@ func TestTimespanSplitByMinuteBucketPlentyExpired(t *testing.T) { } } -func TestTimespanSplitByMinuteBucketPlentyExpiring(t *testing.T) { +func TestTimespanSplitByMinuteBalancePlentyExpiring(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 0, 0, time.UTC)} + mb := &Balance{Value: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 0, 0, time.UTC)} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 60 { t.Error("Not enough minutes on minute bucket split") } @@ -273,12 +273,12 @@ func TestTimespanSplitByMinuteBucketPlentyExpiring(t *testing.T) { } } -func TestTimespanSplitByMinuteBucketPlentyExpiringEnd(t *testing.T) { +func TestTimespanSplitByMinuteBalancePlentyExpiringEnd(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)} + mb := &Balance{Value: 180, ExpirationDate: time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC)} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 120 { t.Error("Not enough minutes on minute bucket split") } @@ -287,12 +287,12 @@ func TestTimespanSplitByMinuteBucketPlentyExpiringEnd(t *testing.T) { } } -func TestTimespanSplitByMinuteBucketScarceExpiringSame(t *testing.T) { +func TestTimespanSplitByMinuteBalanceScarceExpiringSame(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 120, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 0, 0, time.UTC)} + mb := &Balance{Value: 120, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 0, 0, time.UTC)} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 60 { t.Error("Not enough minutes on minute bucket split") } @@ -301,12 +301,12 @@ func TestTimespanSplitByMinuteBucketScarceExpiringSame(t *testing.T) { } } -func TestTimespanSplitByMinuteBucketScarceExpiringDifferentExpFirst(t *testing.T) { +func TestTimespanSplitByMinuteBalanceScarceExpiringDifferentExpFirst(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 140, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 1, 0, time.UTC)} + mb := &Balance{Value: 140, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 1, 0, time.UTC)} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 61 { t.Error("Not enough minutes on minute bucket split: ", ts.MinuteInfo.Quantity) } @@ -315,12 +315,12 @@ func TestTimespanSplitByMinuteBucketScarceExpiringDifferentExpFirst(t *testing.T } } -func TestTimespanSplitByMinuteBucketScarceExpiringDifferentScarceFirst(t *testing.T) { +func TestTimespanSplitByMinuteBalanceScarceExpiringDifferentScarceFirst(t *testing.T) { t1 := time.Date(2013, time.July, 15, 10, 40, 0, 0, time.UTC) t2 := time.Date(2013, time.July, 15, 10, 42, 0, 0, time.UTC) - mb := &MinuteBucket{Seconds: 61, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 30, 0, time.UTC)} + mb := &Balance{Value: 61, ExpirationDate: time.Date(2013, time.July, 15, 10, 41, 30, 0, time.UTC)} ts := TimeSpan{TimeStart: t1, TimeEnd: t2} - newTs := ts.SplitByMinuteBucket(mb) + newTs := ts.SplitByMinuteBalance(mb) if ts.MinuteInfo == nil || ts.MinuteInfo.Quantity != 61 { t.Error("Not enough minutes on minute bucket split") } diff --git a/engine/units_counter.go b/engine/units_counter.go index 2f129e567..fed75af11 100644 --- a/engine/units_counter.go +++ b/engine/units_counter.go @@ -24,39 +24,39 @@ import ( // Amount of a trafic of a certain type type UnitsCounter struct { - Direction string - BalanceId string - Units float64 - MinuteBuckets bucketsorter + Direction string + BalanceId string + Units float64 + MinuteBalances BalanceChain } func (uc *UnitsCounter) initMinuteBuckets(ats []*ActionTrigger) { - uc.MinuteBuckets = make(bucketsorter, 0) + uc.MinuteBalances = make(BalanceChain, 0) for _, at := range ats { acs, err := storageGetter.GetActions(at.ActionsId) if err != nil { continue } for _, a := range acs { - if a.MinuteBucket != nil { - uc.MinuteBuckets = append(uc.MinuteBuckets, a.MinuteBucket.Clone()) + if a.Balance != nil { + uc.MinuteBalances = append(uc.MinuteBalances, a.Balance.Clone()) } } } - uc.MinuteBuckets.Sort() + uc.MinuteBalances.Sort() } -// Adds the minutes from the received minute bucket to an existing bucket if the destination -// is the same or ads the minutye bucket to the list if none matches. +// Adds the minutes from the received minute balance to an existing bucket if the destination +// is the same or ads the minute balance to the list if none matches. func (uc *UnitsCounter) addMinutes(amount float64, prefix string) { - for _, mb := range uc.MinuteBuckets { + for _, mb := range uc.MinuteBalances { d, err := GetDestination(mb.DestinationId) if err != nil { Logger.Err(fmt.Sprintf("Minutes counter: unknown destination: %s", mb.DestinationId)) continue } if _, ok := d.containsPrefix(prefix); ok { - mb.Seconds += amount + mb.Value += amount break } } diff --git a/engine/units_counter_test.go b/engine/units_counter_test.go index e7f8a2c43..9917e4a79 100644 --- a/engine/units_counter_test.go +++ b/engine/units_counter_test.go @@ -22,28 +22,28 @@ import ( "testing" ) -func TestUnitsCounterAddMinuteBucket(t *testing.T) { +func TestUnitsCounterAddBalance(t *testing.T) { uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + Direction: OUTBOUND, + BalanceId: SMS, + Units: 100, + MinuteBalances: []*Balance{&Balance{Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, } uc.addMinutes(20, "test") - if len(uc.MinuteBuckets) != 2 { + if len(uc.MinuteBalances) != 2 { t.Error("Error adding minute bucket!") } } -func TestUnitsCounterAddMinuteBucketExists(t *testing.T) { +func TestUnitsCounterAddBalanceExists(t *testing.T) { uc := &UnitsCounter{ - Direction: OUTBOUND, - BalanceId: SMS, - Units: 100, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + Direction: OUTBOUND, + BalanceId: SMS, + Units: 100, + MinuteBalances: []*Balance{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, } uc.addMinutes(5, "0723") - if len(uc.MinuteBuckets) != 2 || uc.MinuteBuckets[0].Seconds != 15 { + if len(uc.MinuteBalances) != 2 || uc.MinuteBalances[0].Value != 15 { t.Error("Error adding minute bucket!") } } diff --git a/engine/userbalance.go b/engine/userbalance.go index 698bfa69a..177bdd53b 100644 --- a/engine/userbalance.go +++ b/engine/userbalance.go @@ -55,10 +55,10 @@ Structure containing information about user's credit (minutes, cents, sms...).' This can represent a user or a shared group. */ type UserBalance struct { - Id string - Type string // prepaid-postpaid - BalanceMap map[string]BalanceChain - MinuteBuckets []*MinuteBucket + Id string + Type string // prepaid-postpaid + BalanceMap map[string]BalanceChain + //MinuteBuckets []*MinuteBucket UnitCounters []*UnitsCounter ActionTriggers ActionTriggerPriotityList @@ -70,57 +70,60 @@ type UserBalance struct { /* Returns user's available minutes for the specified destination */ -func (ub *UserBalance) getSecondsForPrefix(prefix string) (seconds, credit float64, bucketList bucketsorter) { +func (ub *UserBalance) getSecondsForPrefix(prefix string) (seconds, credit float64, balances BalanceChain) { credit = ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() - if len(ub.MinuteBuckets) == 0 { + if len(ub.BalanceMap[MINUTES]) == 0 { // Logger.Debug("There are no minute buckets to check for user: ", ub.Id) return } - for _, mb := range ub.MinuteBuckets { - if mb.IsExpired() { + for _, b := range ub.BalanceMap[MINUTES] { + if b.IsExpired() { continue } - d, err := GetDestination(mb.DestinationId) + d, err := GetDestination(b.DestinationId) if err != nil { continue } if precision, ok := d.containsPrefix(prefix); ok { - mb.precision = precision - if mb.Seconds > 0 { - bucketList = append(bucketList, mb) + b.precision = precision + if b.Value > 0 { + balances = append(balances, b) } } } - bucketList.Sort() // sorts the buckets according to priority, precision or price - for _, mb := range bucketList { - s := mb.GetSecondsForCredit(credit) - credit -= s * mb.Price + balances.Sort() // sorts the buckets according to priority, precision or price + for _, b := range balances { + s := b.GetSecondsForCredit(credit) + credit -= s * b.SpecialPrice seconds += s } return } // Debit seconds from specified minute bucket -func (ub *UserBalance) debitMinuteBucket(newMb *MinuteBucket) error { +func (ub *UserBalance) debitMinuteBalance(newMb *Balance) error { if newMb == nil { return errors.New("Nil minute bucket!") } + if ub.BalanceMap == nil { + ub.BalanceMap = make(map[string]BalanceChain, 0) + } found := false - for _, mb := range ub.MinuteBuckets { + for _, mb := range ub.BalanceMap[MINUTES] { if mb.IsExpired() { continue } if mb.Equal(newMb) { - mb.Seconds -= newMb.Seconds + mb.Value -= newMb.Value found = true break } } // if it is not found and the Seconds are negative (topup) // then we add it to the list - if !found && newMb.Seconds <= 0 { - newMb.Seconds = -newMb.Seconds - ub.MinuteBuckets = append(ub.MinuteBuckets, newMb) + if !found && newMb.Value <= 0 { + newMb.Value = -newMb.Value + ub.BalanceMap[MINUTES] = append(ub.BalanceMap[MINUTES], newMb) } return nil } @@ -133,7 +136,7 @@ debited and an error will be returned. */ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count bool) error { if count { - ub.countUnits(&Action{BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Seconds: amount, DestinationId: prefix}}) + ub.countUnits(&Action{BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Value: amount, DestinationId: prefix}}) } avaliableNbSeconds, _, bucketList := ub.getSecondsForPrefix(prefix) if avaliableNbSeconds < amount { @@ -144,13 +147,13 @@ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count credit = bc.Clone() } for _, mb := range bucketList { - if mb.Seconds < amount { - if mb.Price > 0 { // debit the money if the bucket has price - credit.Debit(mb.Seconds * mb.Price) + if mb.Value < amount { + if mb.SpecialPrice > 0 { // debit the money if the bucket has price + credit.Debit(mb.Value * mb.SpecialPrice) } } else { - if mb.Price > 0 { // debit the money if the bucket has price - credit.Debit(amount * mb.Price) + if mb.SpecialPrice > 0 { // debit the money if the bucket has price + credit.Debit(amount * mb.SpecialPrice) } break } @@ -165,11 +168,11 @@ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count ub.BalanceMap[CREDIT+OUTBOUND] = credit // credit is > 0 for _, mb := range bucketList { - if mb.Seconds < amount { - amount -= mb.Seconds - mb.Seconds = 0 + if mb.Value < amount { + amount -= mb.Value + mb.Value = 0 } else { - mb.Seconds -= amount + mb.Value -= amount break } } @@ -183,8 +186,8 @@ func (ub *UserBalance) debitBalanceAction(a *Action) float64 { Id: utils.GenUUID(), ExpirationDate: a.ExpirationDate, } - if a.MinuteBucket != nil { - newBalance.Weight = a.MinuteBucket.Weight + if a.Balance != nil { + newBalance.Weight = a.Balance.Weight } found := false id := a.BalanceId + a.Direction @@ -228,14 +231,14 @@ func (ub *UserBalance) executeActionTriggers(a *Action) { for _, uc := range ub.UnitCounters { if uc.BalanceId == at.BalanceId { if at.BalanceId == MINUTES { - for _, mb := range uc.MinuteBuckets { + for _, mb := range uc.MinuteBalances { if strings.Contains(at.ThresholdType, "*max") { - if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue { + if mb.DestinationId == at.DestinationId && mb.Value >= at.ThresholdValue { // run the actions at.Execute(ub) } } else { //MIN - if mb.DestinationId == at.DestinationId && mb.Seconds <= at.ThresholdValue { + if mb.DestinationId == at.DestinationId && mb.Value <= at.ThresholdValue { // run the actions at.Execute(ub) } @@ -259,14 +262,14 @@ func (ub *UserBalance) executeActionTriggers(a *Action) { } else { // BALANCE for _, b := range ub.BalanceMap[at.BalanceId] { if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety - for _, mb := range ub.MinuteBuckets { + for _, mb := range ub.BalanceMap[MINUTES] { if strings.Contains(at.ThresholdType, "*max") { - if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue { + if mb.DestinationId == at.DestinationId && mb.Value >= at.ThresholdValue { // run the actions at.Execute(ub) } } else { //MIN - if mb.DestinationId == at.DestinationId && mb.Seconds <= at.ThresholdValue { + if mb.DestinationId == at.DestinationId && mb.Value <= at.ThresholdValue { // run the actions at.Execute(ub) } @@ -329,8 +332,8 @@ func (ub *UserBalance) countUnits(a *Action) { unitsCounter = &UnitsCounter{BalanceId: a.BalanceId, Direction: direction} ub.UnitCounters = append(ub.UnitCounters, unitsCounter) } - if a.BalanceId == MINUTES && a.MinuteBucket != nil { - unitsCounter.addMinutes(a.MinuteBucket.Seconds, a.MinuteBucket.DestinationId) + if a.BalanceId == MINUTES && a.Balance != nil { + unitsCounter.addMinutes(a.Balance.Value, a.Balance.DestinationId) } else { unitsCounter.Units += a.Units } @@ -346,7 +349,7 @@ func (ub *UserBalance) initMinuteCounters() { continue } for _, a := range acs { - if a.MinuteBucket != nil { + if a.Balance != nil { direction := at.Direction if direction == "" { direction = OUTBOUND @@ -355,11 +358,11 @@ func (ub *UserBalance) initMinuteCounters() { if !exists { uc = &UnitsCounter{BalanceId: MINUTES, Direction: direction} ucTempMap[direction] = uc - uc.MinuteBuckets = bucketsorter{} + uc.MinuteBalances = BalanceChain{} ub.UnitCounters = append(ub.UnitCounters, uc) } - uc.MinuteBuckets = append(uc.MinuteBuckets, a.MinuteBucket.Clone()) - uc.MinuteBuckets.Sort() + uc.MinuteBalances = append(uc.MinuteBalances, a.Balance.Clone()) + uc.MinuteBalances.Sort() } } } @@ -376,9 +379,9 @@ func (ub *UserBalance) CleanExpiredBalancesAndBuckets() { } ub.BalanceMap[key] = bm } - for i := 0; i < len(ub.MinuteBuckets); i++ { - if ub.MinuteBuckets[i].IsExpired() { - ub.MinuteBuckets = append(ub.MinuteBuckets[:i], ub.MinuteBuckets[i+1:]...) + for i := 0; i < len(ub.BalanceMap[MINUTES]); i++ { + if ub.BalanceMap[MINUTES][i].IsExpired() { + ub.BalanceMap[MINUTES] = append(ub.BalanceMap[MINUTES][:i], ub.BalanceMap[MINUTES][i+1:]...) } } } diff --git a/engine/userbalance_test.go b/engine/userbalance_test.go index 0b9f8ab53..af60f3a2c 100644 --- a/engine/userbalance_test.go +++ b/engine/userbalance_test.go @@ -35,7 +35,7 @@ func init() { func populateTestActionsForTriggers() { ats := []*Action{ &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}, - &Action{ActionType: "*topup", BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Weight: 20, Price: 1, Seconds: 10, DestinationId: "NAT"}}, + &Action{ActionType: "*topup", BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Weight: 20, SpecialPrice: 1, Value: 10, DestinationId: "NAT"}}, } storageGetter.SetActions("TEST_ACTIONS", ats) ats1 := []*Action{ @@ -96,9 +96,9 @@ func TestBalanceChainStoreRestore(t *testing.T) { } func TestUserBalanceStorageStoreRestore(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.01, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} storageGetter.SetUserBalance(rifsBalance) ub1, err := storageGetter.GetUserBalance("other") if err != nil || !ub1.BalanceMap[CREDIT+OUTBOUND].Equal(rifsBalance.BalanceMap[CREDIT+OUTBOUND]) { @@ -108,9 +108,9 @@ func TestUserBalanceStorageStoreRestore(t *testing.T) { } func TestGetSecondsForPrefix(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 200}}}} + b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} + ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 200}}}} seconds, credit, bucketList := ub1.getSecondsForPrefix("0723") expected := 110.0 if credit != 200 || seconds != expected || bucketList[0].Weight < bucketList[1].Weight { @@ -118,11 +118,11 @@ func TestGetSecondsForPrefix(t *testing.T) { } } -func TestGetPricedSeconds(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Price: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Price: 1, Weight: 20, DestinationId: "RET"} +func TestGetSpecialPricedSeconds(t *testing.T) { + b1 := &Balance{Value: 10, SpecialPrice: 10, Weight: 10, DestinationId: "NAT"} + b2 := &Balance{Value: 100, SpecialPrice: 1, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} seconds, credit, bucketList := ub1.getSecondsForPrefix("0723") expected := 21.0 if credit != 0 || seconds != expected || len(bucketList) < 2 || bucketList[0].Weight < bucketList[1].Weight { @@ -131,24 +131,24 @@ func TestGetPricedSeconds(t *testing.T) { } func TestUserBalanceStorageStore(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.01, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} storageGetter.SetUserBalance(rifsBalance) result, err := storageGetter.GetUserBalance(rifsBalance.Id) if err != nil || rifsBalance.Id != result.Id || - len(rifsBalance.MinuteBuckets) < 2 || len(result.MinuteBuckets) < 2 || - !(rifsBalance.MinuteBuckets[0].Equal(result.MinuteBuckets[0])) || - !(rifsBalance.MinuteBuckets[1].Equal(result.MinuteBuckets[1])) || + len(rifsBalance.BalanceMap[MINUTES]) < 2 || len(result.BalanceMap[MINUTES]) < 2 || + !(rifsBalance.BalanceMap[MINUTES][0].Equal(result.BalanceMap[MINUTES][0])) || + !(rifsBalance.BalanceMap[MINUTES][1].Equal(result.BalanceMap[MINUTES][1])) || !rifsBalance.BalanceMap[CREDIT+OUTBOUND].Equal(result.BalanceMap[CREDIT+OUTBOUND]) { t.Errorf("Expected %v was %v", rifsBalance, result) } } func TestDebitMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.01, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} result := rifsBalance.debitBalance(CREDIT, 6, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 15 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 15, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) @@ -156,9 +156,9 @@ func TestDebitMoneyBalance(t *testing.T) { } func TestDebitAllMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.01, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} rifsBalance.debitBalance(CREDIT, 21, false) result := rifsBalance.debitBalance(CREDIT, 0, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { @@ -167,9 +167,9 @@ func TestDebitAllMoneyBalance(t *testing.T) { } func TestDebitMoreMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} result := rifsBalance.debitBalance(CREDIT, 22, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != -1 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", -1, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) @@ -177,9 +177,9 @@ func TestDebitMoreMoneyBalance(t *testing.T) { } func TestDebitNegativeMoneyBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} result := rifsBalance.debitBalance(CREDIT, -15, false) if rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 36 || result != rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 36, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) @@ -187,120 +187,120 @@ func TestDebitNegativeMoneyBalance(t *testing.T) { } func TestDebitMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(6, "0723", false) - if b2.Seconds != 94 || err != nil { + if b2.Value != 94 || err != nil { t.Log(err) - t.Errorf("Expected %v was %v", 94, b2.Seconds) + t.Errorf("Expected %v was %v", 94, b2.Value) } } func TestDebitMultipleBucketsMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(105, "0723", false) - if b2.Seconds != 0 || b1.Seconds != 5 || err != nil { + if b2.Value != 0 || b1.Value != 5 || err != nil { t.Log(err) - t.Errorf("Expected %v was %v", 0, b2.Seconds) + t.Errorf("Expected %v was %v", 0, b2.Value) } } func TestDebitAllMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(110, "0723", false) - if b2.Seconds != 0 || b1.Seconds != 0 || err != nil { - t.Errorf("Expected %v was %v", 0, b2.Seconds) + if b2.Value != 0 || b1.Value != 0 || err != nil { + t.Errorf("Expected %v was %v", 0, b2.Value) } } func TestDebitMoreMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(115, "0723", false) - if b2.Seconds != 100 || b1.Seconds != 10 || err == nil { - t.Errorf("Expected %v was %v", 1000, b2.Seconds) + if b2.Value != 100 || b1.Value != 10 || err == nil { + t.Errorf("Expected %v was %v", 1000, b2.Value) } } -func TestDebitPriceMinuteBalance0(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} +func TestDebitSpecialPriceMinuteBalance0(t *testing.T) { + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(5, "0723", false) - if b2.Seconds != 95 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 16 { + if b2.Value != 95 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 16 { t.Errorf("Expected %v was %v", 16, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } } -func TestDebitPriceAllMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} +func TestDebitSpecialPriceAllMinuteBalance(t *testing.T) { + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(21, "0723", false) - if b2.Seconds != 79 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 { + if b2.Value != 79 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 0 { t.Errorf("Expected %v was %v", 0, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } } -func TestDebitPriceMoreMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} +func TestDebitSpecialPriceMoreMinuteBalance(t *testing.T) { + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(25, "0723", false) - if b2.Seconds != 75 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != -4 { - t.Log(b2.Seconds) - t.Log(b1.Seconds) + if b2.Value != 75 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != -4 { + t.Log(b2.Value) + t.Log(b1.Value) t.Log(err) t.Errorf("Expected %v was %v", -4, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } } -func TestDebitPriceMoreMinuteBalancePrepay(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", Type: UB_TYPE_PREPAID, MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} +func TestDebitSpecialPriceMoreMinuteBalancePrepay(t *testing.T) { + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", Type: UB_TYPE_PREPAID, BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(25, "0723", false) expected := 21.0 - if b2.Seconds != 100 || b1.Seconds != 10 || err != AMOUNT_TOO_BIG || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != expected { - t.Log(b2.Seconds) - t.Log(b1.Seconds) + if b2.Value != 100 || b1.Value != 10 || err != AMOUNT_TOO_BIG || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != expected { + t.Log(b2.Value) + t.Log(b1.Value) t.Log(err) t.Errorf("Expected %v was %v", expected, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } } -func TestDebitPriceNegativeMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 1.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} +func TestDebitSpecialPriceNegativeMinuteBalance(t *testing.T) { + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 1.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(-15, "0723", false) - if b2.Seconds != 115 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 36 { + if b2.Value != 115 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 36 { t.Log(b1, b2, err) t.Errorf("Expected %v was %v", 36, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } } func TestDebitNegativeMinuteBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} err := rifsBalance.debitMinutesBalance(-15, "0723", false) - if b2.Seconds != 115 || b1.Seconds != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 21 { + if b2.Value != 115 || b1.Value != 10 || err != nil || rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value != 21 { t.Log(b1, b2, err) t.Errorf("Expected %v was %v", 21, rifsBalance.BalanceMap[CREDIT+OUTBOUND][0].Value) } } func TestDebitSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitBalance(SMS, 12, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 88 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 88, rifsBalance.BalanceMap[SMS+OUTBOUND]) @@ -308,9 +308,9 @@ func TestDebitSMSBalance(t *testing.T) { } func TestDebitAllSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitBalance(SMS, 100, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 0 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 0, rifsBalance.BalanceMap[SMS+OUTBOUND]) @@ -318,9 +318,9 @@ func TestDebitAllSMSBalance(t *testing.T) { } func TestDebitMoreSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitBalance(SMS, 110, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != -10 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", -10, rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value) @@ -328,112 +328,107 @@ func TestDebitMoreSMSBalance(t *testing.T) { } func TestDebitNegativeSMSBalance(t *testing.T) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.0, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.0, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}, SMS + OUTBOUND: BalanceChain{&Balance{Value: 100}}}} result := rifsBalance.debitBalance(SMS, -15, false) if rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value != 115 || result != rifsBalance.BalanceMap[SMS+OUTBOUND][0].Value { t.Errorf("Expected %v was %v", 115, rifsBalance.BalanceMap[SMS+OUTBOUND]) } } -func TestUserBalancedebitMinuteBucket(t *testing.T) { +func TestUserBalancedebitBalance(t *testing.T) { ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS: BalanceChain{&Balance{Value: 14}}, TRAFFIC: BalanceChain{&Balance{Value: 1204}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + Id: "rif", + Type: UB_TYPE_POSTPAID, + BalanceMap: map[string]BalanceChain{SMS: BalanceChain{&Balance{Value: 14}}, TRAFFIC: BalanceChain{&Balance{Value: 1204}}, MINUTES: BalanceChain{&Balance{Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, } - newMb := &MinuteBucket{Weight: 20, Price: 1, DestinationId: "NEW"} - ub.debitMinuteBucket(newMb) - if len(ub.MinuteBuckets) != 3 || ub.MinuteBuckets[2] != newMb { - t.Error("Error adding minute bucket!", len(ub.MinuteBuckets), ub.MinuteBuckets) + newMb := &Balance{Weight: 20, SpecialPrice: 1, DestinationId: "NEW"} + ub.debitMinuteBalance(newMb) + if len(ub.BalanceMap[MINUTES]) != 3 || ub.BalanceMap[MINUTES][2] != newMb { + t.Error("Error adding minute bucket!", len(ub.BalanceMap[MINUTES]), ub.BalanceMap[MINUTES]) } } -func TestUserBalancedebitMinuteBucketExists(t *testing.T) { +func TestUserBalancedebitBalanceExists(t *testing.T) { ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 15, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + Id: "rif", + Type: UB_TYPE_POSTPAID, + BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}, MINUTES: BalanceChain{&Balance{Value: 15, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, } - newMb := &MinuteBucket{Seconds: -10, Weight: 20, Price: 1, DestinationId: "NAT"} - ub.debitMinuteBucket(newMb) - if len(ub.MinuteBuckets) != 2 || ub.MinuteBuckets[0].Seconds != 25 { + newMb := &Balance{Value: -10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"} + ub.debitMinuteBalance(newMb) + if len(ub.BalanceMap[MINUTES]) != 2 || ub.BalanceMap[MINUTES][0].Value != 25 { t.Error("Error adding minute bucket!") } } func TestUserBalanceAddMinuteNil(t *testing.T) { ub := &UserBalance{ - Id: "rif", - Type: UB_TYPE_POSTPAID, - BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, + Id: "rif", + Type: UB_TYPE_POSTPAID, + BalanceMap: map[string]BalanceChain{SMS + OUTBOUND: BalanceChain{&Balance{Value: 14}}, TRAFFIC + OUTBOUND: BalanceChain{&Balance{Value: 1024}}, MINUTES: BalanceChain{&Balance{Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, } - ub.debitMinuteBucket(nil) - if len(ub.MinuteBuckets) != 2 { + ub.debitMinuteBalance(nil) + if len(ub.BalanceMap[MINUTES]) != 2 { t.Error("Error adding minute bucket!") } } func TestUserBalanceAddMinutBucketEmpty(t *testing.T) { - mb1 := &MinuteBucket{Seconds: -10, DestinationId: "NAT"} - mb2 := &MinuteBucket{Seconds: -10, DestinationId: "NAT"} - mb3 := &MinuteBucket{Seconds: -10, DestinationId: "OTHER"} + mb1 := &Balance{Value: -10, DestinationId: "NAT"} + mb2 := &Balance{Value: -10, DestinationId: "NAT"} + mb3 := &Balance{Value: -10, DestinationId: "OTHER"} ub := &UserBalance{} - ub.debitMinuteBucket(mb1) - if len(ub.MinuteBuckets) != 1 { - t.Error("Error adding minute bucket: ", ub.MinuteBuckets) + ub.debitMinuteBalance(mb1) + if len(ub.BalanceMap[MINUTES]) != 1 { + t.Error("Error adding minute bucket: ", ub.BalanceMap[MINUTES]) } - ub.debitMinuteBucket(mb2) - if len(ub.MinuteBuckets) != 1 || ub.MinuteBuckets[0].Seconds != 20 { - t.Error("Error adding minute bucket: ", ub.MinuteBuckets) + ub.debitMinuteBalance(mb2) + if len(ub.BalanceMap[MINUTES]) != 1 || ub.BalanceMap[MINUTES][0].Value != 20 { + t.Error("Error adding minute bucket: ", ub.BalanceMap[MINUTES]) } - ub.debitMinuteBucket(mb3) - if len(ub.MinuteBuckets) != 2 { - t.Error("Error adding minute bucket: ", ub.MinuteBuckets) + ub.debitMinuteBalance(mb3) + if len(ub.BalanceMap[MINUTES]) != 2 { + t.Error("Error adding minute bucket: ", ub.BalanceMap[MINUTES]) } } func TestUserBalanceExecuteTriggeredActions(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(&Action{BalanceId: CREDIT, Units: 1}) - if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 { - t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds) + if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.BalanceMap[MINUTES][0].Value != 20 { + t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES][0].Value) } // are set to executed ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}) - if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 { - t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds) + if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.BalanceMap[MINUTES][0].Value != 20 { + t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES][0].Value) } // we can reset them ub.resetActionTriggers(nil) ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}) - if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 120 || ub.MinuteBuckets[0].Seconds != 30 { - t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds) + if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 120 || ub.BalanceMap[MINUTES][0].Value != 30 { + t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES][0].Value) } } func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) { ub := &UserBalance{ Id: "TEST_UB", - BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}}, + BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}, MINUTES: BalanceChain{&Balance{Value: 10, Weight: 20, SpecialPrice: 1, DestinationId: "NAT"}, &Balance{Weight: 10, SpecialPrice: 10, SpecialPriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}}, UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, - MinuteBuckets: []*MinuteBucket{&MinuteBucket{Seconds: 10, Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: PRICE_ABSOLUTE, DestinationId: "RET"}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: TRIGGER_MIN_COUNTER, ActionsId: "TEST_ACTIONS"}}, } ub.countUnits(&Action{BalanceId: CREDIT, Units: 1}) - if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.MinuteBuckets[0].Seconds != 20 { - t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.MinuteBuckets[0].Seconds) + if ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 110 || ub.BalanceMap[MINUTES][0].Value != 20 { + t.Error("Error executing triggered actions", ub.BalanceMap[CREDIT+OUTBOUND][0].Value, ub.BalanceMap[MINUTES][0].Value) } } @@ -446,8 +441,6 @@ func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) { } ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}) if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 1 || ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 10 { - t.Log(ub.BalanceMap[CREDIT+OUTBOUND][0]) - t.Log(ub.BalanceMap[CREDIT+OUTBOUND][1]) t.Error("Error executing triggered actions in order", ub.BalanceMap[CREDIT+OUTBOUND]) } } @@ -458,17 +451,16 @@ func TestCleanExpired(t *testing.T) { BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{ &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, &Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, - &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}}}, - MinuteBuckets: []*MinuteBucket{ - &MinuteBucket{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, - &MinuteBucket{ExpirationDate: time.Now().Add(10 * time.Second)}, - }, + &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}}, MINUTES: BalanceChain{ + &Balance{ExpirationDate: time.Date(2013, 7, 18, 14, 33, 0, 0, time.UTC)}, + &Balance{ExpirationDate: time.Now().Add(10 * time.Second)}, + }}, } ub.CleanExpiredBalancesAndBuckets() if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 2 { t.Error("Error cleaning expired balances!") } - if len(ub.MinuteBuckets) != 1 { + if len(ub.BalanceMap[MINUTES]) != 1 { t.Error("Error cleaning expired minute buckets!") } } @@ -521,10 +513,10 @@ func TestUserBalanceUnitCountingOutboundInbound(t *testing.T) { func BenchmarkGetSecondForPrefix(b *testing.B) { b.StopTimer() - b1 := &MinuteBucket{Seconds: 10, Price: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Price: 1, Weight: 20, DestinationId: "RET"} + b1 := &Balance{Value: 10, SpecialPrice: 10, Weight: 10, DestinationId: "NAT"} + b2 := &Balance{Value: 100, SpecialPrice: 1, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + ub1 := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} b.StartTimer() for i := 0; i < b.N; i++ { ub1.getSecondsForPrefix("0723") @@ -532,9 +524,9 @@ func BenchmarkGetSecondForPrefix(b *testing.B) { } func BenchmarkUserBalanceStorageStoreRestore(b *testing.B) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, Price: 0.01, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, Price: 0.0, DestinationId: "RET"} - rifsBalance := &UserBalance{Id: "other", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, SpecialPrice: 0.01, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, SpecialPrice: 0.0, DestinationId: "RET"} + rifsBalance := &UserBalance{Id: "other", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} for i := 0; i < b.N; i++ { storageGetter.SetUserBalance(rifsBalance) storageGetter.GetUserBalance(rifsBalance.Id) @@ -542,9 +534,9 @@ func BenchmarkUserBalanceStorageStoreRestore(b *testing.B) { } func BenchmarkGetSecondsForPrefix(b *testing.B) { - b1 := &MinuteBucket{Seconds: 10, Weight: 10, DestinationId: "NAT"} - b2 := &MinuteBucket{Seconds: 100, Weight: 20, DestinationId: "RET"} - ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", MinuteBuckets: []*MinuteBucket{b1, b2}, BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} + b1 := &Balance{Value: 10, Weight: 10, DestinationId: "NAT"} + b2 := &Balance{Value: 100, Weight: 20, DestinationId: "RET"} + ub1 := &UserBalance{Id: "OUT:CUSTOMER_1:rif", BalanceMap: map[string]BalanceChain{MINUTES: BalanceChain{b1, b2}, CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 21}}}} for i := 0; i < b.N; i++ { ub1.getSecondsForPrefix("0723") }