converted minutebucket to balance, compiled but test fail

This commit is contained in:
Radu Ioan Fericean
2013-09-05 19:14:22 +03:00
parent f82a2c58a8
commit 931c6efb1a
18 changed files with 419 additions and 569 deletions

View File

@@ -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)
}

View File

@@ -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))

View File

@@ -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
}

View File

@@ -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)
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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()

View File

@@ -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)
}

View File

@@ -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 <http://www.gnu.org/licenses/>
*/
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)
}

View File

@@ -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)

View File

@@ -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)
}

View File

@@ -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},
}

View File

@@ -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
}

View File

@@ -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")
}

View File

@@ -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
}
}

View File

@@ -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!")
}
}

View File

@@ -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:]...)
}
}
}

View File

@@ -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")
}