From f3f9d03c80364fffe700fdced5dcb49aa112ae7b Mon Sep 17 00:00:00 2001 From: Radu Ioan Fericean Date: Thu, 5 Sep 2013 20:17:14 +0300 Subject: [PATCH] before switching to MINUTES+OUTBOUND --- engine/action.go | 6 ++-- engine/actions_test.go | 27 +++++++++-------- engine/balances.go | 15 +++++----- ...inute_buckets_test.go => balances_test.go} | 14 ++++----- engine/storage_sql.go | 2 +- engine/tpimporter_csv.go | 12 ++++---- engine/userbalance.go | 14 ++++----- engine/userbalance_test.go | 30 +++++++++---------- 8 files changed, 61 insertions(+), 59 deletions(-) rename engine/{minute_buckets_test.go => balances_test.go} (86%) diff --git a/engine/action.go b/engine/action.go index 1dcf33baf..744d9dea2 100644 --- a/engine/action.go +++ b/engine/action.go @@ -35,9 +35,9 @@ type Action struct { ExpirationString string Weight float64 Balance *Balance - DestinationTag, RateType string // From here for import/load purposes only - ExpirationDate time.Time - Units, RateValue, MinutesWeight float64 + destinationTag, rateType string // From here for import/load purposes only + expirationDate time.Time + units, rateValue, minutesWeight float64 } const ( diff --git a/engine/actions_test.go b/engine/actions_test.go index 47d27bae2..69b90da20 100644 --- a/engine/actions_test.go +++ b/engine/actions_test.go @@ -605,8 +605,9 @@ func TestActionResetPrepaid(t *testing.T) { if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 0 || len(ub.UnitCounters) != 0 || - len(ub.BalanceMap[MINUTES]) != 0 || + ub.BalanceMap[MINUTES][0].Value != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { + t.Log(ub.BalanceMap) t.Error("Reset prepaid action failed!") } } @@ -623,7 +624,7 @@ func TestActionResetPostpaid(t *testing.T) { if ub.Type != UB_TYPE_POSTPAID || ub.BalanceMap[CREDIT].GetTotalValue() != 0 || len(ub.UnitCounters) != 0 || - len(ub.BalanceMap[MINUTES]) != 0 || + ub.BalanceMap[MINUTES][0].Value != 0 || ub.ActionTriggers[0].Executed == true || ub.ActionTriggers[1].Executed == true { t.Error("Reset postpaid action failed!") } @@ -637,7 +638,7 @@ func TestActionTopupResetCredit(t *testing.T) { UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, 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} + a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} topupResetAction(ub, a) if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 10 || @@ -650,9 +651,11 @@ func TestActionTopupResetCredit(t *testing.T) { func TestActionTopupResetMinutes(t *testing.T) { ub := &UserBalance{ - Id: "TEST_UB", - Type: UB_TYPE_PREPAID, - 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"}}}, + Id: "TEST_UB", + Type: UB_TYPE_PREPAID, + 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}}, 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}}, } @@ -664,7 +667,7 @@ func TestActionTopupResetMinutes(t *testing.T) { len(ub.UnitCounters) != 1 || len(ub.BalanceMap[MINUTES]) != 1 || ub.ActionTriggers[0].Executed != true || ub.ActionTriggers[1].Executed != true { - t.Error("Topup reset minutes action failed!", ub.BalanceMap[MINUTES][0]) + t.Errorf("Topup reset minutes action failed: %v", ub.BalanceMap) } } @@ -676,7 +679,7 @@ func TestActionTopupCredit(t *testing.T) { UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, 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} + a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} topupAction(ub, a) if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 110 || @@ -715,7 +718,7 @@ func TestActionDebitCredit(t *testing.T) { UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, 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} + a := &Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}} debitAction(ub, a) if ub.Type != UB_TYPE_PREPAID || ub.BalanceMap[CREDIT+OUTBOUND].GetTotalValue() != 90 || @@ -889,13 +892,13 @@ func TestActionTimingLogging(t *testing.T) { } func TestActionMakeNegative(t *testing.T) { - a := &Action{Units: 10} + a := &Action{Balance: &Balance{Value: 10}} genericMakeNegative(a) - if a.Units > 0 { + if a.Balance.Value > 0 { t.Error("Failed to make negative: ", a) } genericMakeNegative(a) - if a.Units > 0 { + if a.Balance.Value > 0 { t.Error("Failed to preserve negative: ", a) } } diff --git a/engine/balances.go b/engine/balances.go index 036113c42..20de21adb 100644 --- a/engine/balances.go +++ b/engine/balances.go @@ -40,7 +40,7 @@ type Balance struct { func (b *Balance) Equal(o *Balance) bool { return b.ExpirationDate.Equal(o.ExpirationDate) && b.Weight == o.Weight && - b.SpecialPrice == b.SpecialPrice && + b.SpecialPrice == o.SpecialPrice && b.DestinationId == o.DestinationId } @@ -50,12 +50,13 @@ func (b *Balance) IsExpired() bool { func (b *Balance) Clone() *Balance { return &Balance{ - Id: b.Id, - Value: b.Value, - SpecialPrice: b.SpecialPrice, - DestinationId: b.DestinationId, - ExpirationDate: b.ExpirationDate, - Weight: b.Weight, + Id: b.Id, + Value: b.Value, + SpecialPrice: b.SpecialPrice, + SpecialPriceType: b.SpecialPriceType, + DestinationId: b.DestinationId, + ExpirationDate: b.ExpirationDate, + Weight: b.Weight, } } diff --git a/engine/minute_buckets_test.go b/engine/balances_test.go similarity index 86% rename from engine/minute_buckets_test.go rename to engine/balances_test.go index b218d2334..24db6b1a4 100644 --- a/engine/minute_buckets_test.go +++ b/engine/balances_test.go @@ -23,7 +23,7 @@ import ( "testing" ) -func TestMinutBucketSortWeight(t *testing.T) { +func TestBalanceSortWeight(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 2, SpecialPrice: 2} mb2 := &Balance{Weight: 2, precision: 1, SpecialPrice: 1} var bs BalanceChain @@ -34,7 +34,7 @@ func TestMinutBucketSortWeight(t *testing.T) { } } -func TestMinutBucketSortPrecision(t *testing.T) { +func TestBalanceSortPrecision(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 2, SpecialPrice: 2} mb2 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1} var bs BalanceChain @@ -45,7 +45,7 @@ func TestMinutBucketSortPrecision(t *testing.T) { } } -func TestMinutBucketSortSpecialPrice(t *testing.T) { +func TestBalanceSortSpecialPrice(t *testing.T) { mb1 := &Balance{Weight: 1, precision: 1, SpecialPrice: 1} mb2 := &Balance{Weight: 1, precision: 1, SpecialPrice: 2} var bs BalanceChain @@ -56,19 +56,19 @@ func TestMinutBucketSortSpecialPrice(t *testing.T) { } } -func TestMinutBucketEqual(t *testing.T) { +func TestBalanceEqual(t *testing.T) { 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) + t.Error("Equal failure!", mb1 == mb2, mb3) } } -func TestMinutBucketClone(t *testing.T) { +func TestBalanceClone(t *testing.T) { 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) + t.Errorf("Cloning failure: \n%v\n%v", mb1, mb2) } } diff --git a/engine/storage_sql.go b/engine/storage_sql.go index 2668849d8..58bbc2faf 100644 --- a/engine/storage_sql.go +++ b/engine/storage_sql.go @@ -541,7 +541,7 @@ func (self *SQLStorage) SetTPActions(tpid string, acts map[string][]*Action) err } qry += fmt.Sprintf("('%s','%s','%s','%s','%s',%f,'%s','%s','%s',%f,%f,%f)", tpid, actId, act.ActionType, act.BalanceId, act.Direction, act.Balance.Value, act.ExpirationString, - act.DestinationTag, act.RateType, act.RateValue, act.MinutesWeight, act.Weight) + act.destinationTag, act.rateType, act.rateValue, act.minutesWeight, act.Weight) i++ } } diff --git a/engine/tpimporter_csv.go b/engine/tpimporter_csv.go index 56824abd4..09bd764ad 100644 --- a/engine/tpimporter_csv.go +++ b/engine/tpimporter_csv.go @@ -317,12 +317,12 @@ func (self *TPCSVImporter) importActions(fn string) error { ActionType: actionType, BalanceId: balanceType, Direction: direction, - Units: units, - ExpirationDate: expiryTime, - DestinationTag: destTag, - RateType: rateType, - RateValue: rateValue, - MinutesWeight: minutesWeight, + units: units, + expirationDate: expiryTime, + destinationTag: destTag, + rateType: rateType, + rateValue: rateValue, + minutesWeight: minutesWeight, Weight: weight, } if err := self.StorDb.SetTPActions(self.TPid, map[string][]*Action{actId: []*Action{act}}); err != nil { diff --git a/engine/userbalance.go b/engine/userbalance.go index 177bdd53b..bed8ac76a 100644 --- a/engine/userbalance.go +++ b/engine/userbalance.go @@ -182,23 +182,21 @@ func (ub *UserBalance) debitMinutesBalance(amount float64, prefix string, count // Debits some amount of user's specified balance adding the balance if it does not exists. // Returns the remaining credit in user's balance. func (ub *UserBalance) debitBalanceAction(a *Action) float64 { - newBalance := &Balance{ - Id: utils.GenUUID(), - ExpirationDate: a.ExpirationDate, - } + newBalance := &Balance{Id: utils.GenUUID()} if a.Balance != nil { + newBalance.ExpirationDate = a.Balance.ExpirationDate newBalance.Weight = a.Balance.Weight } found := false id := a.BalanceId + a.Direction for _, b := range ub.BalanceMap[id] { if b.Equal(newBalance) { - b.Value -= a.Units + b.Value -= a.Balance.Value found = true } } if !found { - newBalance.Value -= a.Units + newBalance.Value -= a.Balance.Value ub.BalanceMap[id] = append(ub.BalanceMap[id], newBalance) } return ub.BalanceMap[a.BalanceId+OUTBOUND].GetTotalValue() @@ -209,7 +207,7 @@ Debits some amount of user's specified balance. Returns the remaining credit in */ func (ub *UserBalance) debitBalance(balanceId string, amount float64, count bool) float64 { if count { - ub.countUnits(&Action{BalanceId: balanceId, Direction: OUTBOUND, Units: amount}) + ub.countUnits(&Action{BalanceId: balanceId, Direction: OUTBOUND, Balance: &Balance{Value: amount}}) } ub.BalanceMap[balanceId+OUTBOUND].Debit(amount) return ub.BalanceMap[balanceId+OUTBOUND].GetTotalValue() @@ -335,7 +333,7 @@ func (ub *UserBalance) countUnits(a *Action) { if a.BalanceId == MINUTES && a.Balance != nil { unitsCounter.addMinutes(a.Balance.Value, a.Balance.DestinationId) } else { - unitsCounter.Units += a.Units + unitsCounter.Units += a.Balance.Value } ub.executeActionTriggers(nil) } diff --git a/engine/userbalance_test.go b/engine/userbalance_test.go index af60f3a2c..421354a1a 100644 --- a/engine/userbalance_test.go +++ b/engine/userbalance_test.go @@ -34,12 +34,12 @@ func init() { func populateTestActionsForTriggers() { ats := []*Action{ - &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}, + &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}, &Action{ActionType: "*topup", BalanceId: MINUTES, Direction: OUTBOUND, Balance: &Balance{Weight: 20, SpecialPrice: 1, Value: 10, DestinationId: "NAT"}}, } storageGetter.SetActions("TEST_ACTIONS", ats) ats1 := []*Action{ - &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Units: 10, Weight: 20}, + &Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}, Weight: 20}, &Action{ActionType: "*reset_prepaid", Weight: 10}, } storageGetter.SetActions("TEST_ACTIONS_ORDER", ats1) @@ -402,18 +402,18 @@ func TestUserBalanceExecuteTriggeredActions(t *testing.T) { UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS"}}, } - ub.countUnits(&Action{BalanceId: CREDIT, Units: 1}) + ub.countUnits(&Action{BalanceId: CREDIT, Balance: &Balance{Value: 1}}) 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}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 1}}) 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}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) 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) } @@ -426,7 +426,7 @@ func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) { UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: TRIGGER_MIN_COUNTER, ActionsId: "TEST_ACTIONS"}}, } - ub.countUnits(&Action{BalanceId: CREDIT, Units: 1}) + ub.countUnits(&Action{BalanceId: CREDIT, Balance: &Balance{Value: 1}}) 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) } @@ -439,7 +439,7 @@ func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) { UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}}, ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: TRIGGER_MAX_COUNTER, ActionsId: "TEST_ACTIONS_ORDER"}}, } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 1}}) if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 1 || ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 10 { t.Error("Error executing triggered actions in order", ub.BalanceMap[CREDIT+OUTBOUND]) } @@ -467,11 +467,11 @@ func TestCleanExpired(t *testing.T) { func TestUserBalanceUnitCounting(t *testing.T) { ub := &UserBalance{} - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 20 { t.Error("Error counting units") } @@ -479,15 +479,15 @@ func TestUserBalanceUnitCounting(t *testing.T) { func TestUserBalanceUnitCountingOutbound(t *testing.T) { ub := &UserBalance{} - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 20 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 30 { t.Error("Error counting units") } @@ -495,15 +495,15 @@ func TestUserBalanceUnitCountingOutbound(t *testing.T) { func TestUserBalanceUnitCountingOutboundInbound(t *testing.T) { ub := &UserBalance{} - ub.countUnits(&Action{BalanceId: CREDIT, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 10 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 1 && ub.UnitCounters[0].BalanceId != CREDIT || ub.UnitCounters[0].Units != 20 { t.Error("Error counting units") } - ub.countUnits(&Action{BalanceId: CREDIT, Direction: INBOUND, Units: 10}) + ub.countUnits(&Action{BalanceId: CREDIT, Direction: INBOUND, Balance: &Balance{Value: 10}}) if len(ub.UnitCounters) != 2 && ub.UnitCounters[1].BalanceId != CREDIT || ub.UnitCounters[0].Units != 20 || ub.UnitCounters[1].Units != 10 { t.Error("Error counting units") }