This commit is contained in:
DanB
2013-07-24 09:29:29 +02:00
23 changed files with 394 additions and 196 deletions

View File

@@ -39,17 +39,17 @@ type Action struct {
}
const (
LOG = "LOG"
RESET_TRIGGERS = "RESET_TRIGGERS"
SET_POSTPAID = "SET_POSTPAID"
RESET_POSTPAID = "RESET_POSTPAID"
SET_PREPAID = "SET_PREPAID"
RESET_PREPAID = "RESET_PREPAID"
TOPUP_RESET = "TOPUP_RESET"
TOPUP = "TOPUP"
DEBIT = "DEBIT"
RESET_COUNTER = "RESET_COUNTER"
RESET_COUNTERS = "RESET_COUNTERS"
LOG = "*log"
RESET_TRIGGERS = "*reset_triggers"
SET_POSTPAID = "*set_postpaid"
RESET_POSTPAID = "*reset_postpaid"
SET_PREPAID = "*set_prepaid"
RESET_PREPAID = "*reset_prepaid"
TOPUP_RESET = "*topup_reset"
TOPUP = "*topup"
DEBIT = "*debit"
RESET_COUNTER = "*reset_counter"
RESET_COUNTERS = "*reset_counters"
)
type actionTypeFunc func(*UserBalance, *Action) error

View File

@@ -757,7 +757,7 @@ func TestActionTimingLogging(t *testing.T) {
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
Prices: PriceGroups{&Price{0, 1.0}},
PricedUnits: 60,
RateIncrements: 1,
}

View File

@@ -28,7 +28,7 @@ The struture that is saved to storage.
*/
type ActivationPeriod struct {
ActivationTime time.Time
Intervals []*Interval
Intervals IntervalList
}
type xCachedActivationPeriods struct {
@@ -37,21 +37,15 @@ type xCachedActivationPeriods struct {
*cache2go.XEntry
}
/*
Adds one ore more intervals to the internal interval list.
*/
func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
ap.Intervals = append(ap.Intervals, is...)
}
/*
Adds one ore more intervals to the internal interval list only if it is not allready in the list.
*/
func (ap *ActivationPeriod) AddIntervalIfNotPresent(is ...*Interval) {
func (ap *ActivationPeriod) AddInterval(is ...*Interval) {
for _, i := range is {
found := false
for _, ei := range ap.Intervals {
if i.Equal(ei) {
(&ei.Prices).AddPrice(i.Prices...)
found = true
break
}

View File

@@ -8,7 +8,7 @@ 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
but WITH*out ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
@@ -20,7 +20,6 @@ package rater
import (
"encoding/json"
//"log"
"reflect"
"testing"
"time"
@@ -28,7 +27,7 @@ import (
func TestApRestoreFromStorage(t *testing.T) {
cd := &CallDescriptor{
Direction: "OUT",
Direction: OUTBOUND,
TOR: "0",
Tenant: "CUSTOMER_1",
Subject: "rif:from:tm",
@@ -49,7 +48,7 @@ func TestApStoreRestoreJson(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result, _ := json.Marshal(ap)
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":[2],\"MonthDays\":[1],\"WeekDays\":[3,4],\"StartTime\":\"14:30:00\",\"EndTime\":\"15:00:00\",\"Weight\":0,\"ConnectFee\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"Prices\":null,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
@@ -66,7 +65,7 @@ func TestApStoreRestoreBlank(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result, _ := json.Marshal(ap)
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"Price\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
expected := "{\"ActivationTime\":\"2012-02-01T14:30:01Z\",\"Intervals\":[{\"Years\":null,\"Months\":null,\"MonthDays\":null,\"WeekDays\":null,\"StartTime\":\"\",\"EndTime\":\"\",\"Weight\":0,\"ConnectFee\":0,\"PricedUnits\":0,\"RateIncrements\":0,\"Prices\":null,\"RoundingMethod\":\"\",\"RoundingDecimals\":0}]}"
if string(result) != expected {
t.Errorf("Expected %q was %q", expected, result)
}
@@ -78,7 +77,7 @@ func TestApStoreRestoreBlank(t *testing.T) {
}
func TestFallbackDirect(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "41"}
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "41"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
@@ -86,7 +85,7 @@ func TestFallbackDirect(t *testing.T) {
}
func TestFallbackMultiple(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "vdf", Subject: "fall", Destination: "0723045"}
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "fall", Destination: "0723045"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
@@ -94,7 +93,7 @@ func TestFallbackMultiple(t *testing.T) {
}
func TestFallbackWithBackTrace(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"}
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "CUSTOMER_2", Subject: "danb:87.139.12.167", Destination: "4123"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
@@ -102,7 +101,7 @@ func TestFallbackWithBackTrace(t *testing.T) {
}
func TestFallbackDefault(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "vdf", Subject: "one", Destination: "0723"}
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "one", Destination: "0723"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 1 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
@@ -110,7 +109,7 @@ func TestFallbackDefault(t *testing.T) {
}
func TestFallbackNoInfiniteLoop(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "rif", Destination: "0721"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 0 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
@@ -118,7 +117,7 @@ func TestFallbackNoInfiniteLoop(t *testing.T) {
}
func TestFallbackNoInfiniteLoopSelf(t *testing.T) {
cd := &CallDescriptor{TOR: "0", Direction: "OUT", Tenant: "vdf", Subject: "inf", Destination: "0721"}
cd := &CallDescriptor{TOR: "0", Direction: OUTBOUND, Tenant: "vdf", Subject: "inf", Destination: "0721"}
cd.LoadActivationPeriods()
if len(cd.ActivationPeriods) != 0 {
t.Error("Error restoring activation periods: ", len(cd.ActivationPeriods))
@@ -142,17 +141,39 @@ func TestApAddIntervalIfNotPresent(t *testing.T) {
StartTime: "14:30:00",
EndTime: "15:00:00"}
ap := &ActivationPeriod{}
ap.AddIntervalIfNotPresent(i1)
ap.AddIntervalIfNotPresent(i2)
ap.AddInterval(i1)
ap.AddInterval(i2)
if len(ap.Intervals) != 1 {
t.Error("Wronfully appended interval ;)")
}
ap.AddIntervalIfNotPresent(i3)
ap.AddInterval(i3)
if len(ap.Intervals) != 2 {
t.Error("Wronfully not appended interval ;)")
}
}
func TestApAddIntervalGroups(t *testing.T) {
i1 := &Interval{
Prices: PriceGroups{&Price{0, 1}},
}
i2 := &Interval{
Prices: PriceGroups{&Price{30, 2}},
}
i3 := &Interval{
Prices: PriceGroups{&Price{30, 2}},
}
ap := &ActivationPeriod{}
ap.AddInterval(i1)
ap.AddInterval(i2)
ap.AddInterval(i3)
if len(ap.Intervals) != 1 {
t.Error("Wronfully appended interval ;)")
}
if len(ap.Intervals[0].Prices) != 2 {
t.Error("Group prices not formed: ", ap.Intervals[0].Prices)
}
}
/**************************** Benchmarks *************************************/
func BenchmarkActivationPeriodStoreRestoreJson(b *testing.B) {

View File

@@ -8,7 +8,7 @@ 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
but WITH*out ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
@@ -27,14 +27,14 @@ import (
func TestSingleResultMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 00, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 17, 01, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 60 {
t.Errorf("expected 60 was %v", cc1.Cost)
}
t1 = time.Date(2012, time.February, 2, 17, 01, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 17, 02, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
if cc2.Cost != 60 {
t.Errorf("expected 60 was %v", cc2.Cost)
@@ -51,7 +51,7 @@ func TestSingleResultMerge(t *testing.T) {
func TestMultipleResultMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 00, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 60 {
t.Errorf("expected 60 was %v", cc1.Cost)
@@ -61,7 +61,7 @@ func TestMultipleResultMerge(t *testing.T) {
}
t1 = time.Date(2012, time.February, 2, 18, 00, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
if cc2.Cost != 30 {
t.Errorf("expected 30 was %v", cc2.Cost)
@@ -81,14 +81,14 @@ func TestMultipleResultMerge(t *testing.T) {
func TestMultipleInputLeftMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 90 {
t.Errorf("expected 90 was %v", cc1.Cost)
}
t1 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 18, 02, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
if cc2.Cost != 30 {
t.Errorf("expected 30 was %v", cc2.Cost)
@@ -105,14 +105,14 @@ func TestMultipleInputLeftMerge(t *testing.T) {
func TestMultipleInputRightMerge(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 58, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc1, _ := cd.GetCost()
if cc1.Cost != 60 {
t.Errorf("expected 60 was %v", cc1.Cost)
}
t1 = time.Date(2012, time.February, 2, 17, 59, 0, 0, time.UTC)
t2 = time.Date(2012, time.February, 2, 18, 01, 0, 0, time.UTC)
cd = &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd = &CallDescriptor{Direction: OUTBOUND, TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cc2, _ := cd.GetCost()
if cc2.Cost != 90 {
t.Errorf("expected 90 was %v", cc2.Cost)

View File

@@ -40,7 +40,7 @@ func init() {
const (
RECURSION_MAX_DEPTH = 10
FALLBACK_SUBJECT = "*all"
FALLBACK_SUBJECT = "*any"
FALLBACK_SEP = ";"
)
@@ -189,17 +189,13 @@ func (cd *CallDescriptor) GetKey() string {
return fmt.Sprintf("%s:%s:%s:%s", cd.Direction, cd.Tenant, cd.TOR, cd.Subject)
}
/*
Splits the call descriptor timespan into sub time spans according to the activation periods intervals.
*/
func (cd *CallDescriptor) splitInTimeSpans() (timespans []*TimeSpan) {
return cd.splitTimeSpan(&TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd})
}
/*
Splits the received timespan into sub time spans according to the activation periods intervals.
*/
func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeSpan) {
func (cd *CallDescriptor) splitInTimeSpans(firstSpan *TimeSpan) (timespans []*TimeSpan) {
if firstSpan == nil {
firstSpan = &TimeSpan{TimeStart: cd.TimeStart, TimeEnd: cd.TimeEnd}
}
timespans = append(timespans, firstSpan)
// split on (free) minute buckets
if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
@@ -250,11 +246,15 @@ func (cd *CallDescriptor) splitTimeSpan(firstSpan *TimeSpan) (timespans []*TimeS
// split on price intervals
for i := 0; i < len(timespans); i++ {
if timespans[i].MinuteInfo != nil {
continue
continue // cont try to split timespans payed with minutes
}
ap := timespans[i].ActivationPeriod
//timespans[i].ActivationPeriod = nil
ap.Intervals.Sort()
for _, interval := range ap.Intervals {
if timespans[i].Interval != nil && timespans[i].Interval.Weight < interval.Weight {
continue // if the timespan has an interval than it already has a heigher weight
}
newTs := timespans[i].SplitByInterval(interval)
if newTs != nil {
newTs.ActivationPeriod = ap
@@ -274,7 +274,7 @@ func (cd *CallDescriptor) GetCost() (*CallCost, error) {
Logger.Err(fmt.Sprintf("error getting cost for key %v: %v", cd.GetUserBalanceKey(), err))
return &CallCost{}, err
}
timespans := cd.splitInTimeSpans()
timespans := cd.splitInTimeSpans(nil)
cost := 0.0
connectionFee := 0.0
@@ -336,7 +336,7 @@ func (cd *CallDescriptor) GetMaxSessionTime() (seconds float64, err error) {
for i := 0; i < 10; i++ {
maxDuration, _ := time.ParseDuration(fmt.Sprintf("%vs", maxSessionSeconds-availableSeconds))
ts := &TimeSpan{TimeStart: now, TimeEnd: now.Add(maxDuration)}
timespans := cd.splitTimeSpan(ts)
timespans := cd.splitInTimeSpans(ts)
cost := 0.0
for i, ts := range timespans {

View File

@@ -8,7 +8,7 @@ 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
but WITH*out ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
@@ -30,7 +30,7 @@ func init() {
func populateDB() {
minu := &UserBalance{
Id: "OUT:vdf:minu",
Id: "*out:vdf:minu",
Type: UB_TYPE_PREPAID,
BalanceMap: map[string]BalanceChain{CREDIT: BalanceChain{&Balance{Value: 0}}},
MinuteBuckets: []*MinuteBucket{
@@ -39,7 +39,7 @@ func populateDB() {
},
}
broker := &UserBalance{
Id: "OUT:vdf:broker",
Id: "*out:vdf:broker",
Type: UB_TYPE_PREPAID,
MinuteBuckets: []*MinuteBucket{
&MinuteBucket{Seconds: 20, DestinationId: "NAT", Weight: 10, Price: 1},
@@ -58,10 +58,10 @@ func populateDB() {
func TestSplitSpans(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.LoadActivationPeriods()
timespans := cd.splitInTimeSpans()
timespans := cd.splitInTimeSpans(nil)
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
t.Error("Wrong number of timespans: ", len(timespans))
@@ -71,10 +71,10 @@ func TestSplitSpans(t *testing.T) {
func TestRedisSplitSpans(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257", TimeStart: t1, TimeEnd: t2}
cd.LoadActivationPeriods()
timespans := cd.splitInTimeSpans()
timespans := cd.splitInTimeSpans(nil)
if len(timespans) != 2 {
t.Log(cd.ActivationPeriods)
t.Error("Wrong number of timespans: ", len(timespans))
@@ -84,7 +84,7 @@ func TestRedisSplitSpans(t *testing.T) {
func TestGetCost(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 0}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -95,7 +95,7 @@ func TestGetCost(t *testing.T) {
func TestGetCostNoConnectFee(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 1}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2, LoopIndex: 1}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -106,7 +106,7 @@ func TestGetCostNoConnectFee(t *testing.T) {
func TestGetCostAccount(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Account: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Account: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -117,7 +117,7 @@ func TestGetCostAccount(t *testing.T) {
func TestFullDestNotFound(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0256", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -129,7 +129,7 @@ func TestFullDestNotFound(t *testing.T) {
func TestSubjectNotFound(t *testing.T) {
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "not_exiting", Destination: "025740532", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "not_exiting", Destination: "025740532", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -141,7 +141,7 @@ func TestSubjectNotFound(t *testing.T) {
func TestMultipleActivationPeriods(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 2700, ConnectFee: 1}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -153,7 +153,7 @@ func TestMultipleActivationPeriods(t *testing.T) {
func TestSpansMultipleActivationPeriods(t *testing.T) {
t1 := time.Date(2012, time.February, 7, 23, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 0, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 1200, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -164,7 +164,7 @@ func TestSpansMultipleActivationPeriods(t *testing.T) {
func TestLessThanAMinute(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 23, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 23, 50, 30, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0257308200", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0257", Cost: 15, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -175,7 +175,7 @@ func TestLessThanAMinute(t *testing.T) {
func TestUniquePrice(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 23, 50, 21, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723045326", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723045326", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "rif", Destination: "0723", Cost: 1810.5, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -186,7 +186,7 @@ func TestUniquePrice(t *testing.T) {
func TestMinutesCost(t *testing.T) {
t1 := time.Date(2012, time.February, 8, 22, 50, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 8, 22, 51, 50, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", TimeStart: t1, TimeEnd: t2}
result, _ := cd.GetCost()
expected := &CallCost{Tenant: "vdf", Subject: "minutosu", Destination: "0723", Cost: 55, ConnectFee: 0}
if result.Cost != expected.Cost || result.ConnectFee != expected.ConnectFee {
@@ -195,7 +195,7 @@ func TestMinutesCost(t *testing.T) {
}
func TestMaxSessionTimeNoUserBalance(t *testing.T) {
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", Amount: 1000}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0723", Amount: 1000}
result, err := cd.GetMaxSessionTime()
if result != 1000 || err == nil {
t.Errorf("Expected %v was %v (%v)", 1000, result, err)
@@ -203,7 +203,7 @@ func TestMaxSessionTimeNoUserBalance(t *testing.T) {
}
func TestMaxSessionTimeWithUserBalance(t *testing.T) {
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "minu", Destination: "0723", Amount: 1000}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "minu", Destination: "0723", Amount: 1000}
result, err := cd.GetMaxSessionTime()
expected := 300.0
if result != expected || err != nil {
@@ -212,7 +212,7 @@ func TestMaxSessionTimeWithUserBalance(t *testing.T) {
}
func TestMaxSessionTimeWithUserBalanceAccount(t *testing.T) {
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "minu_from_tm", Account: "minu", Destination: "0723", Amount: 1000}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "minu_from_tm", Account: "minu", Destination: "0723", Amount: 1000}
result, err := cd.GetMaxSessionTime()
expected := 300.0
if result != expected || err != nil {
@@ -221,7 +221,7 @@ func TestMaxSessionTimeWithUserBalanceAccount(t *testing.T) {
}
func TestMaxSessionTimeNoCredit(t *testing.T) {
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "broker", Destination: "0723", Amount: 5400}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "broker", Destination: "0723", Amount: 5400}
result, err := cd.GetMaxSessionTime()
if result != 100 || err != nil {
t.Errorf("Expected %v was %v", 100, result)
@@ -233,7 +233,7 @@ func BenchmarkStorageGetting(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
storageGetter.GetRatingProfile(cd.GetKey())
@@ -244,7 +244,7 @@ func BenchmarkStorageRestoring(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.LoadActivationPeriods()
@@ -255,7 +255,7 @@ func BenchmarkStorageGetCost(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetCost()
@@ -266,11 +266,11 @@ func BenchmarkSplitting(b *testing.B) {
b.StopTimer()
t1 := time.Date(2012, time.February, 2, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 2, 18, 30, 0, 0, time.UTC)
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "rif", Destination: "0256", TimeStart: t1, TimeEnd: t2}
cd.LoadActivationPeriods()
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.splitInTimeSpans()
cd.splitInTimeSpans(nil)
}
}
@@ -285,7 +285,7 @@ func BenchmarkStorageSingleGetSessionTime(b *testing.B) {
func BenchmarkStorageMultipleGetSessionTime(b *testing.B) {
b.StopTimer()
cd := &CallDescriptor{Direction: "OUT", TOR: "0", Tenant: "vdf", Subject: "minutosu", Destination: "0723", Amount: 5400}
cd := &CallDescriptor{Direction: "*out", TOR: "0", Tenant: "vdf", Subject: "minutosu", Destination: "0723", Amount: 5400}
b.StartTimer()
for i := 0; i < b.N; i++ {
cd.GetMaxSessionTime()

View File

@@ -61,7 +61,7 @@ func (ys Years) Contains(year int) (result bool) {
// Parse Years elements from string separated by sep.
func (ys *Years) Parse(input, sep string) {
switch input {
case "*all", "":
case "*any", "":
*ys = []int{}
default:
elements := strings.Split(input, sep)
@@ -75,7 +75,7 @@ func (ys *Years) Parse(input, sep string) {
func (ys Years) Serialize(sep string) string {
if len(ys) == 0 {
return "*all"
return "*any"
}
var yStr string
for idx, yr := range ys {
@@ -124,7 +124,7 @@ func (m Months) Contains(month time.Month) (result bool) {
// Loades Month elemnents from a string separated by sep.
func (m *Months) Parse(input, sep string) {
switch input {
case "*all":
case "*any":
*m = allMonths
case "*none": // Apier cannot receive empty string, hence using meta-tag
*m = []time.Month{}
@@ -146,7 +146,7 @@ func (m Months) Serialize(sep string) string {
return "*none"
}
if reflect.DeepEqual(m, Months(allMonths)) {
return "*all"
return "*any"
}
var mStr string
for idx, mt := range m {
@@ -195,7 +195,7 @@ func (md MonthDays) Contains(monthDay int) (result bool) {
// Parse MonthDay elements from string separated by sep.
func (md *MonthDays) Parse(input, sep string) {
switch input {
case "*all":
case "*any":
*md = allMonthDays
case "":
*md = []int{}
@@ -215,7 +215,7 @@ func (md MonthDays) Serialize(sep string) string {
return "*none"
}
if reflect.DeepEqual(md, MonthDays(allMonthDays)) {
return "*all"
return "*any"
}
var mdsStr string
for idx, mDay := range md {
@@ -263,7 +263,7 @@ func (wd WeekDays) Contains(weekDay time.Weekday) (result bool) {
func (wd *WeekDays) Parse(input, sep string) {
switch input {
case "*all":
case "*any":
*wd = allWeekDays
case "":
*wd = []time.Weekday{}
@@ -283,7 +283,7 @@ func (wd WeekDays) Serialize(sep string) string {
return "*none"
}
if reflect.DeepEqual(wd, WeekDays(allWeekDays)) {
return "*all"
return "*any"
}
var wdStr string
for idx, d := range wd {

View File

@@ -67,7 +67,7 @@ func TestWeekDayStoreRestoreJson(t *testing.T) {
func TestYearsSerialize(t *testing.T) {
ys := &Years{}
yString := ys.Serialize(";")
expectString := "*all"
expectString := "*any"
if expectString != yString {
t.Errorf("Expected: %s, got: %s", expectString, yString)
}
@@ -94,7 +94,7 @@ func TestMonthsSerialize(t *testing.T) {
}
mths1 := Months(allMonths)
mString1 := mths1.Serialize(";")
expectString1 := "*all"
expectString1 := "*any"
if expectString1 != mString1 {
t.Errorf("Expected: %s, got: %s", expectString1, mString1)
}
@@ -121,7 +121,7 @@ func TestMonthDaysSerialize(t *testing.T) {
}
mds1 := MonthDays(allMonthDays)
mdsString1 := mds1.Serialize(";")
expectString1 := "*all"
expectString1 := "*any"
if expectString1 != mdsString1 {
t.Errorf("Expected: %s, got: %s", expectString1, mdsString1)
}
@@ -148,7 +148,7 @@ func TestWeekDaysSerialize(t *testing.T) {
}
wds1 := WeekDays(allWeekDays)
wdsString1 := wds1.Serialize(";")
expectString1 := "*all"
expectString1 := "*any"
if expectString1 != wdsString1 {
t.Errorf("Expected: %s, got: %s", expectString1, wdsString1)
}

View File

@@ -23,24 +23,79 @@ import (
"github.com/cgrates/cgrates/utils"
"math"
"reflect"
"sort"
"strconv"
"strings"
"time"
// "log"
)
/*
Defines a time interval for which a certain set of prices will apply
*/
type Interval struct {
Years Years
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee, Price, PricedUnits, RateIncrements float64
RoundingMethod string
RoundingDecimals int
Years Years
Months Months
MonthDays MonthDays
WeekDays WeekDays
StartTime, EndTime string // ##:##:## format
Weight, ConnectFee, PricedUnits, RateIncrements float64
Prices PriceGroups // GroupInterval (start time): Price
RoundingMethod string
RoundingDecimals int
}
type Price struct {
StartSecond float64
Value float64
}
func (p *Price) Equal(o *Price) bool {
return p.StartSecond == o.StartSecond && p.Value == o.Value
}
type PriceGroups []*Price
func (pg PriceGroups) Len() int {
return len(pg)
}
func (pg PriceGroups) Swap(i, j int) {
pg[i], pg[j] = pg[j], pg[i]
}
func (pg PriceGroups) Less(i, j int) bool {
return pg[i].StartSecond < pg[j].StartSecond
}
func (pg PriceGroups) Sort() {
sort.Sort(pg)
}
func (pg PriceGroups) Equal(og PriceGroups) bool {
if len(pg) != len(og) {
return false
}
for i := 0; i < len(pg); i++ {
if !pg[i].Equal(og[i]) {
return false
}
}
return true
}
func (pg *PriceGroups) AddPrice(ps ...*Price) {
for _, p := range ps {
found := false
for _, op := range *pg {
if op.Equal(p) {
found = true
break
}
}
if !found {
*pg = append(*pg, p)
}
}
}
/*
@@ -137,12 +192,42 @@ func (i *Interval) Equal(o *Interval) bool {
i.EndTime == o.EndTime
}
func (i *Interval) GetCost(duration float64) (cost float64) {
func (i *Interval) GetCost(duration, startSecond float64) (cost float64) {
if i.PricedUnits != 0 {
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.Price / i.PricedUnits)
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * (i.GetPrice(startSecond) / i.PricedUnits)
} else {
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.Price
cost = math.Ceil(duration/i.RateIncrements) * i.RateIncrements * i.GetPrice(startSecond)
}
return utils.Round(cost, i.RoundingDecimals, i.RoundingMethod)
}
// gets the price for a the provided start second
func (i *Interval) GetPrice(startSecond float64) float64 {
i.Prices.Sort()
for index, price := range i.Prices {
if price.StartSecond <= startSecond && (index == len(i.Prices)-1 ||
i.Prices[index+1].StartSecond > startSecond) {
return price.Value
}
}
return -1
}
// Structure to store intervals according to weight
type IntervalList []*Interval
func (il IntervalList) Len() int {
return len(il)
}
func (il IntervalList) Swap(i, j int) {
il[i], il[j] = il[j], il[i]
}
func (il IntervalList) Less(i, j int) bool {
return il[i].Weight < il[j].Weight
}
func (il IntervalList) Sort() {
sort.Sort(il)
}

View File

@@ -235,7 +235,7 @@ func (csvr *CSVReader) LoadRates() (err error) {
continue
}
var r *Rate
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7])
r, err = NewRate(record[0], record[1], record[2], record[3], record[4], record[5], record[6], record[7], record[8])
if err != nil {
return err
}
@@ -306,7 +306,7 @@ func (csvr *CSVReader) LoadDestinationRateTimings() (err error) {
if _, exists := csvr.activationPeriods[tag]; !exists {
csvr.activationPeriods[tag] = &ActivationPeriod{}
}
csvr.activationPeriods[tag].AddIntervalIfNotPresent(rt.GetInterval(dr))
csvr.activationPeriods[tag].AddInterval(rt.GetInterval(dr))
}
}
return

View File

@@ -19,6 +19,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>
package rater
import (
//"log"
"testing"
)
@@ -38,17 +39,17 @@ RET,0723
RET,0724
`
timings = `
WORKDAYS_00,*all,*all,*all,1;2;3;4;5,00:00:00
WORKDAYS_18,*all,*all,*all,1;2;3;4;5,18:00:00
WEEKENDS,*all,*all,*all,6;7,00:00:00
WORKDAYS_00,*any,*any,*any,1;2;3;4;5,00:00:00
WORKDAYS_18,*any,*any,*any,1;2;3;4;5,18:00:00
WEEKENDS,*any,*any,*any,6;7,00:00:00
ONE_TIME_RUN,2012,,,,*asap
`
rates = `
R1,0,0.2,60,1,*middle,2,10
R2,0,0.1,60,1,*middle,2,10
R3,0,0.05,60,1,*middle,2,10
R4,1,1,1,1,*up,2,10
R5,0,0.5,1,1,*down,2,10
R1,0,0.2,60,1,0,*middle,2,10
R2,0,0.1,60,1,0,*middle,2,10
R3,0,0.05,60,1,0,*middle,2,10
R4,1,1,1,1,0,*up,2,10
R5,0,0.5,1,1,0,*down,2,10
`
destinationRates = `
RT_STANDARD,GERMANY,R1
@@ -71,30 +72,30 @@ EVENING,P2,WORKDAYS_18,10
EVENING,P2,WEEKENDS,10
`
ratingProfiles = `
CUSTOMER_1,0,OUT,rif:from:tm,danb,PREMIUM,2012-01-01T00:00:00Z
CUSTOMER_1,0,OUT,rif:from:tm,danb,STANDARD,2012-02-28T00:00:00Z
CUSTOMER_2,0,OUT,danb:87.139.12.167,danb,STANDARD,2012-01-01T00:00:00Z
CUSTOMER_1,0,OUT,danb,,PREMIUM,2012-01-01T00:00:00Z
vdf,0,OUT,rif,,EVENING,2012-01-01T00:00:00Z
vdf,0,OUT,rif,,EVENING,2012-02-28T00:00:00Z
vdf,0,OUT,minu,,EVENING,2012-01-01T00:00:00Z
vdf,0,OUT,*all,,EVENING,2012-02-28T00:00:00Z
vdf,0,OUT,one,,STANDARD,2012-02-28T00:00:00Z
vdf,0,OUT,inf,inf,STANDARD,2012-02-28T00:00:00Z
vdf,0,OUT,fall,one|rif,PREMIUM,2012-02-28T00:00:00Z
CUSTOMER_1,0,*out,rif:from:tm,danb,PREMIUM,2012-01-01T00:00:00Z
CUSTOMER_1,0,*out,rif:from:tm,danb,STANDARD,2012-02-28T00:00:00Z
CUSTOMER_2,0,*out,danb:87.139.12.167,danb,STANDARD,2012-01-01T00:00:00Z
CUSTOMER_1,0,*out,danb,,PREMIUM,2012-01-01T00:00:00Z
vdf,0,*out,rif,,EVENING,2012-01-01T00:00:00Z
vdf,0,*out,rif,,EVENING,2012-02-28T00:00:00Z
vdf,0,*out,minu,,EVENING,2012-01-01T00:00:00Z
vdf,0,*out,*any,,EVENING,2012-02-28T00:00:00Z
vdf,0,*out,one,,STANDARD,2012-02-28T00:00:00Z
vdf,0,*out,inf,inf,STANDARD,2012-02-28T00:00:00Z
vdf,0,*out,fall,one|rif,PREMIUM,2012-02-28T00:00:00Z
`
actions = `
MINI,TOPUP,MINUTES,OUT,100,1374239002,NAT,ABSOLUTE,0,10,10
MINI,TOPUP,MINUTES,*out,100,1374239002,NAT,*absolute,0,10,10
`
actionTimings = `
MORE_MINUTES,MINI,ONE_TIME_RUN,10
`
actionTriggers = `
STANDARD_TRIGGER,MINUTES,OUT,COUNTER,10,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,MINUTES,OUT,BALANCE,200,GERMANY,SOME_2,10
STANDARD_TRIGGER,MINUTES,*out,COUNTER,10,GERMANY_O2,SOME_1,10
STANDARD_TRIGGER,MINUTES,*out,BALANCE,200,GERMANY,SOME_2,10
`
accountActions = `
vdf,minitsboy,OUT,MORE_MINUTES,STANDARD_TRIGGER
vdf,minitsboy,*out,MORE_MINUTES,STANDARD_TRIGGER
`
)
@@ -143,6 +144,9 @@ func TestLoadDestinationRateTimings(t *testing.T) {
if len(csvr.activationPeriods) != 4 {
t.Error("Failed to load rate timings: ", csvr.activationPeriods)
}
//for _, ap := range csvr.activationPeriods {
//log.Print(ap.Intervals[0].Prices[1])
//}
}
func TestLoadRatingProfiles(t *testing.T) {

View File

@@ -172,7 +172,7 @@ func (dbr *DbReader) LoadDestinationRateTimings() error {
if !exists {
dbr.activationPeriods[rt.Tag] = &ActivationPeriod{}
}
dbr.activationPeriods[rt.Tag].AddIntervalIfNotPresent(rt.GetInterval(dr))
dbr.activationPeriods[rt.Tag].AddInterval(rt.GetInterval(dr))
}
}
return nil
@@ -243,7 +243,7 @@ func (dbr *DbReader) LoadRatingProfileByTag(tag string) error {
if _, exists := activationPeriods[destrateTiming.Tag]; !exists {
activationPeriods[destrateTiming.Tag] = &ActivationPeriod{}
}
activationPeriods[destrateTiming.Tag].AddIntervalIfNotPresent(destrateTiming.GetInterval(drate))
activationPeriods[destrateTiming.Tag].AddInterval(destrateTiming.GetInterval(drate))
dm, err := dbr.storDb.GetTpDestinations(dbr.tpid, drate.DestinationsTag)
if err != nil {
return err

View File

@@ -43,14 +43,14 @@ type TPLoader interface {
}
type Rate struct {
Tag string
ConnectFee, Price, PricedUnits, RateIncrements float64
RoundingMethod string
RoundingDecimals int
Weight float64
Tag string
ConnectFee, Price, PricedUnits, RateIncrements, GroupInterval float64
RoundingMethod string
RoundingDecimals int
Weight float64
}
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, roundingMethod, roundingDecimals, weight string) (r *Rate, err error) {
func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, groupInterval, roundingMethod, roundingDecimals, weight string) (r *Rate, err error) {
cf, err := strconv.ParseFloat(connectFee, 64)
if err != nil {
log.Printf("Error parsing connect fee from: %v", connectFee)
@@ -61,6 +61,11 @@ func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, roundingMethod
log.Printf("Error parsing price from: %v", price)
return
}
gi, err := strconv.ParseFloat(groupInterval, 64)
if err != nil {
log.Printf("Error parsing group interval from: %v", price)
return
}
pu, err := strconv.ParseFloat(pricedUnits, 64)
if err != nil {
log.Printf("Error parsing priced units from: %v", pricedUnits)
@@ -86,6 +91,7 @@ func NewRate(tag, connectFee, price, pricedUnits, rateIncrements, roundingMethod
Tag: tag,
ConnectFee: cf,
Price: p,
GroupInterval: gi,
PricedUnits: pu,
RateIncrements: ri,
Weight: wght,
@@ -153,7 +159,7 @@ func (rt *DestinationRateTiming) GetInterval(dr *DestinationRate) (i *Interval)
StartTime: rt.timing.StartTime,
Weight: rt.Weight,
ConnectFee: dr.Rate.ConnectFee,
Price: dr.Rate.Price,
Prices: PriceGroups{&Price{dr.Rate.GroupInterval, dr.Rate.Price}},
PricedUnits: dr.Rate.PricedUnits,
RateIncrements: dr.Rate.RateIncrements,
}

View File

@@ -35,8 +35,8 @@ type MinuteBucket struct {
}
const (
PERCENT = "PERCENT"
ABSOLUTE = "ABSOLUTE"
PERCENT = "*percent"
ABSOLUTE = "*absolute"
)
// Returns the available number of seconds for a specified credit

View File

@@ -456,6 +456,38 @@ func (d *Destination) Restore(input string) error {
return nil
}
func (pg PriceGroups) Store() (result string, err error) {
for _, p := range pg {
result += strconv.FormatFloat(p.StartSecond, 'f', -1, 64) + ":" + strconv.FormatFloat(p.Value, 'f', -1, 64) + ","
}
result = strings.TrimRight(result, ",")
return
}
func (pg *PriceGroups) Restore(input string) error {
elements := strings.Split(input, ",")
for _, element := range elements {
priceElements := strings.Split(element, ":")
if len(priceElements) != 2 {
continue
}
ss, err := strconv.ParseFloat(priceElements[0], 64)
if err != nil {
return err
}
v, err := strconv.ParseFloat(priceElements[1], 64)
if err != nil {
return err
}
price := &Price{
StartSecond: ss,
Value: v,
}
*pg = append(*pg, price)
}
return nil
}
func (i *Interval) Store() (result string, err error) {
str, err := i.Years.Store()
if err != nil {
@@ -481,7 +513,11 @@ func (i *Interval) Store() (result string, err error) {
result += i.EndTime + ";"
result += strconv.FormatFloat(i.Weight, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.ConnectFee, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.Price, 'f', -1, 64) + ";"
ps, err := i.Prices.Store()
if err != nil {
return "", err
}
result += ps + ";"
result += strconv.FormatFloat(i.PricedUnits, 'f', -1, 64) + ";"
result += strconv.FormatFloat(i.RateIncrements, 'f', -1, 64) + ";"
result += i.RoundingMethod + ";"
@@ -510,7 +546,10 @@ func (i *Interval) Restore(input string) error {
i.EndTime = is[5]
i.Weight, _ = strconv.ParseFloat(is[6], 64)
i.ConnectFee, _ = strconv.ParseFloat(is[7], 64)
i.Price, _ = strconv.ParseFloat(is[8], 64)
err := (&i.Prices).Restore(is[8])
if err != nil {
return err
}
i.PricedUnits, _ = strconv.ParseFloat(is[9], 64)
i.RateIncrements, _ = strconv.ParseFloat(is[10], 64)
i.RoundingMethod = is[11]

View File

@@ -35,10 +35,6 @@ func TestSimpleMarshallerApStoreRestore(t *testing.T) {
ap := &ActivationPeriod{ActivationTime: d}
ap.AddInterval(i)
result, err := ap.Store()
expected := "2012-02-01T14:30:01Z|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0;;0"
if err != nil || !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %q was %q", expected, result)
}
ap1 := &ActivationPeriod{}
err = ap1.Restore(result)
if err != nil || !reflect.DeepEqual(ap, ap1) {
@@ -68,10 +64,6 @@ func TestRpStoreRestore(t *testing.T) {
rp := &RatingProfile{FallbackKey: "test"}
rp.AddActivationPeriodIfNotPresent("0723", ap)
result, err := rp.Store()
expected := "test>0723=2012-02-01T14:30:01Z|;2;1;3,4;14:30:00;15:00:00;0;0;0;0;0;;0"
if err != nil || !reflect.DeepEqual(result, expected) {
t.Errorf("Expected %q was %q", expected, result)
}
rp1 := &RatingProfile{}
err = rp1.Restore(result)
if err != nil || !reflect.DeepEqual(rp, rp1) {
@@ -88,7 +80,7 @@ func TestActionTimingStoreRestore(t *testing.T) {
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
Prices: PriceGroups{&Price{0, 1.0}},
PricedUnits: 60,
RateIncrements: 1,
}
@@ -101,9 +93,6 @@ func TestActionTimingStoreRestore(t *testing.T) {
ActionsId: "Commando",
}
r, err := at.Store()
if err != nil || r != "some uuid|test|one,two,three|;1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1;;0|10|Commando" {
t.Errorf("Error serializing action timing: %v", string(r))
}
o := &ActionTiming{}
err = o.Restore(r)
if err != nil || !reflect.DeepEqual(o, at) {
@@ -117,13 +106,13 @@ func TestActionTriggerStoreRestore(t *testing.T) {
BalanceId: CREDIT,
Direction: OUTBOUND,
ThresholdValue: 100.0,
ThresholdType: "MAX_COUNTER",
ThresholdType: "*max_counter",
DestinationId: "NAT",
Weight: 10.0,
ActionsId: "Commando",
}
r, err := at.Store()
if err != nil || r != "some_uuid;MONETARY;OUT;NAT;Commando;100;MAX_COUNTER;10;false" {
if err != nil || r != "some_uuid;*monetary;*out;NAT;Commando;100;*max_counter;10;false" {
t.Errorf("Error serializing action trigger: %v", string(r))
}
o := &ActionTrigger{}
@@ -142,14 +131,11 @@ func TestIntervalStoreRestore(t *testing.T) {
EndTime: "00:00:00",
Weight: 10.0,
ConnectFee: 0.0,
Price: 1.0,
Prices: PriceGroups{&Price{0, 1777.0}},
PricedUnits: 60,
RateIncrements: 1,
}
r, err := i.Store()
if err != nil || r != ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5;18:00:00;00:00:00;10;0;1;60;1;;0" {
t.Errorf("Error serializing interval: %v", string(r))
}
o := &Interval{}
err = o.Restore(r)
if err != nil || !reflect.DeepEqual(o, i) {
@@ -158,10 +144,10 @@ func TestIntervalStoreRestore(t *testing.T) {
}
func TestIntervalRestoreFromString(t *testing.T) {
s := ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0.2;60;0;;1"
s := ";1,2,3,4,5,6,7,8,9,10,11,12;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31;1,2,3,4,5,6,0;00:00:00;;10;0;0:0.2;60;0;;1"
i := Interval{}
err := i.Restore(s)
if err != nil || i.Price != 0.2 {
if err != nil || !i.Prices.Equal(PriceGroups{&Price{0, 0.2}}) {
t.Errorf("Error restoring inteval period from string %+v", i)
}
}

View File

@@ -744,7 +744,7 @@ func (self *SQLStorage) SetTPAccountActions(tpid string, aa map[string]*AccountA
if _, err := self.Db.Exec(qry); err != nil {
return err
}
return nil
return nil
}
func (self *SQLStorage) GetTPAccountActionIds(tpid string) ([]string, error) {
@@ -932,9 +932,9 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*Rate, error) {
defer rows.Close()
for rows.Next() {
var tag, roundingMethod string
var connect_fee, rate, priced_units, rate_increments, weight float64
var connect_fee, rate, priced_units, rate_increments, group_interval, weight float64
var roundingDecimals int
if err := rows.Scan(&tag, &connect_fee, &rate, &priced_units, &rate_increments, &roundingMethod, &roundingDecimals, &weight); err != nil {
if err := rows.Scan(&tag, &connect_fee, &rate, &priced_units, &rate_increments, &group_interval, &roundingMethod, &roundingDecimals, &weight); err != nil {
return nil, err
}
r := &Rate{
@@ -943,6 +943,7 @@ func (self *SQLStorage) GetTpRates(tpid, tag string) (map[string]*Rate, error) {
Price: rate,
PricedUnits: priced_units,
RateIncrements: rate_increments,
GroupInterval: group_interval,
RoundingMethod: roundingMethod,
RoundingDecimals: roundingDecimals,
Weight: weight,

View File

@@ -32,6 +32,7 @@ type TimeSpan struct {
ActivationPeriod *ActivationPeriod
Interval *Interval
MinuteInfo *MinuteInfo
CallDuration float64 // the call duration so far till TimeEnd
}
// Holds the bonus minute information related to a specified timespan
@@ -63,7 +64,7 @@ func (ts *TimeSpan) getCost(cd *CallDescriptor) (cost float64) {
if i.RateIncrements == 0 {
i.RateIncrements = 1
}
cost = i.GetCost(duration)
cost = i.GetCost(duration, ts.GetGroupStart())
// if userBalance, err := cd.getUserBalance(); err == nil && userBalance != nil {
// userBalance.mux.RLock()
// if percentageDiscount, err := userBalance.getVolumeDiscount(cd.Destination, INBOUND); err == nil && percentageDiscount > 0 {
@@ -90,7 +91,7 @@ func (ts *TimeSpan) SetInterval(i *Interval) {
if ts.Interval == nil || ts.Interval.Weight < i.Weight {
ts.Interval = i
}
if ts.Interval.Weight == i.Weight && i.Price < ts.Interval.Price {
if ts.Interval.Weight == i.Weight && i.GetPrice(ts.GetGroupStart()) < ts.Interval.GetPrice(ts.GetGroupStart()) {
ts.Interval = i
}
}
@@ -102,12 +103,28 @@ a new timespan starting from the end of the received one.
The interval will attach itself to the timespan that overlaps the interval.
*/
func (ts *TimeSpan) SplitByInterval(i *Interval) (nts *TimeSpan) {
//Logger.Debug("here: ", ts, " +++ ", i)
// if the span is not in interval return nil
if !(i.Contains(ts.TimeStart) || i.Contains(ts.TimeEnd)) {
//Logger.Debug("Not in interval")
return
}
// split by GroupStart
i.Prices.Sort()
for _, price := range i.Prices {
if ts.GetGroupStart() < price.StartSecond && ts.GetGroupEnd() >= price.StartSecond {
ts.SetInterval(i)
splitTime := ts.TimeStart.Add(time.Duration(price.StartSecond-ts.GetGroupStart()) * time.Second)
nts = &TimeSpan{TimeStart: splitTime, TimeEnd: ts.TimeEnd}
nts.SetInterval(i)
nts.CallDuration = ts.CallDuration
ts.CallDuration = ts.CallDuration - nts.GetDuration().Seconds()
ts.TimeEnd = splitTime
return
}
}
// if the span is enclosed in the interval try to set as new interval and return nil
if i.Contains(ts.TimeStart) && i.Contains(ts.TimeEnd) {
//Logger.Debug("All in interval")
@@ -187,3 +204,14 @@ func (ts *TimeSpan) SplitByMinuteBucket(mb *MinuteBucket) (newTs *TimeSpan) {
return
}
func (ts *TimeSpan) GetGroupStart() float64 {
if ts.CallDuration == 0 {
return 0
}
return ts.CallDuration - ts.GetDuration().Seconds()
}
func (ts *TimeSpan) GetGroupEnd() float64 {
return ts.CallDuration
}

View File

@@ -192,7 +192,7 @@ func TestTimespanGetCost(t *testing.T) {
if ts1.getCost(cd) != 0 {
t.Error("No interval and still kicking")
}
ts1.Interval = &Interval{Price: 1}
ts1.Interval = &Interval{Prices: PriceGroups{&Price{0, 1.0}}}
if ts1.getCost(cd) != 600 {
t.Error("Expected 10 got ", ts1.getCost(cd))
}
@@ -204,9 +204,9 @@ func TestTimespanGetCost(t *testing.T) {
}
func TestSetInterval(t *testing.T) {
i1 := &Interval{Price: 1}
i1 := &Interval{Prices: PriceGroups{&Price{0, 1.0}}}
ts1 := TimeSpan{Interval: i1}
i2 := &Interval{Price: 2}
i2 := &Interval{Prices: PriceGroups{&Price{0, 2.0}}}
ts1.SetInterval(i2)
if ts1.Interval != i1 {
t.Error("Smaller price interval should win")
@@ -329,3 +329,37 @@ func TestTimespanSplitByMinuteBucketScarceExpiringDifferentScarceFirst(t *testin
t.Error("Missing extra timespan on minute bucket split")
}
}
func TestTimespanSplitGroupedRates(t *testing.T) {
i := &Interval{
EndTime: "17:59:00",
Prices: PriceGroups{&Price{0, 1}, &Price{900, 2}},
RateIncrements: 1,
}
t1 := time.Date(2012, time.February, 3, 17, 30, 0, 0, time.UTC)
t2 := time.Date(2012, time.February, 3, 18, 00, 0, 0, time.UTC)
ts := &TimeSpan{TimeStart: t1, TimeEnd: t2, CallDuration: 1800}
oldDuration := ts.GetDuration()
nts := ts.SplitByInterval(i)
if ts.TimeStart != t1 || ts.TimeEnd != time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC) {
t.Error("Incorrect first half", ts)
}
if nts.TimeStart != time.Date(2012, time.February, 3, 17, 45, 00, 0, time.UTC) || nts.TimeEnd != t2 {
t.Error("Incorrect second half", nts)
}
if ts.Interval != i {
t.Error("Interval not attached correctly")
}
c1 := ts.Interval.GetCost(ts.GetDuration().Seconds(), ts.GetGroupStart())
c2 := nts.Interval.GetCost(nts.GetDuration().Seconds(), nts.GetGroupStart())
if c1 != 900 || c2 != 1800 {
t.Error("Wrong costs: ", c1, c2)
}
if ts.GetDuration().Seconds() != 15*60 || nts.GetDuration().Seconds() != 15*60 {
t.Error("Wrong durations.for Intervals", ts.GetDuration().Seconds(), nts.GetDuration().Seconds())
}
if ts.GetDuration().Seconds()+nts.GetDuration().Seconds() != oldDuration.Seconds() {
t.Errorf("The duration has changed: %v + %v != %v", ts.GetDuration().Seconds(), nts.GetDuration().Seconds(), oldDuration.Seconds())
}
}

View File

@@ -31,7 +31,7 @@ func TestUnitsCounterStoreRestore(t *testing.T) {
MinuteBuckets: []*MinuteBucket{&MinuteBucket{Weight: 20, Price: 1, DestinationId: "NAT"}, &MinuteBucket{Weight: 10, Price: 10, PriceType: ABSOLUTE, DestinationId: "RET"}},
}
r, err := uc.Store()
if err != nil || r != "OUT/SMS/100/0;20;1;;NAT,0;10;10;ABSOLUTE;RET" {
if err != nil || r != "*out/*sms/100/0;20;1;;NAT,0;10;10;*absolute;RET" {
t.Errorf("Error serializing units counter: %v", string(r))
}
o := &UnitsCounter{}

View File

@@ -27,17 +27,17 @@ import (
)
const (
UB_TYPE_POSTPAID = "postpaid"
UB_TYPE_PREPAID = "prepaid"
UB_TYPE_POSTPAID = "*postpaid"
UB_TYPE_PREPAID = "*prepaid"
// Direction type
INBOUND = "IN"
OUTBOUND = "OUT"
INBOUND = "*in"
OUTBOUND = "*out"
// Balance types
CREDIT = "MONETARY"
SMS = "SMS"
TRAFFIC = "INTERNET"
TRAFFIC_TIME = "INTERNET_TIME"
MINUTES = "MINUTES"
CREDIT = "*monetary"
SMS = "*sms"
TRAFFIC = "*internet"
TRAFFIC_TIME = "*internet_time"
MINUTES = "*minutes"
)
/*
@@ -295,12 +295,12 @@ func (ub *UserBalance) executeActionTriggers(a *Action) {
at.ThresholdValue != a.MinuteBucket.Price))) {
continue
}
if strings.Contains(at.ThresholdType, "COUNTER") {
if strings.Contains(at.ThresholdType, "counter") {
for _, uc := range ub.UnitCounters {
if uc.BalanceId == at.BalanceId {
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety
for _, mb := range uc.MinuteBuckets {
if strings.Contains(at.ThresholdType, "MAX") {
if strings.Contains(at.ThresholdType, "*max") {
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
// run the actions
at.Execute(ub)
@@ -313,7 +313,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) {
}
}
} else {
if strings.Contains(at.ThresholdType, "MAX") {
if strings.Contains(at.ThresholdType, "*max") {
if uc.Units >= at.ThresholdValue {
// run the actions
at.Execute(ub)
@@ -331,7 +331,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) {
for _, b := range ub.BalanceMap[at.BalanceId] {
if at.BalanceId == MINUTES && at.DestinationId != "" { // last check adds safety
for _, mb := range ub.MinuteBuckets {
if strings.Contains(at.ThresholdType, "MAX") {
if strings.Contains(at.ThresholdType, "*max") {
if mb.DestinationId == at.DestinationId && mb.Seconds >= at.ThresholdValue {
// run the actions
at.Execute(ub)
@@ -344,7 +344,7 @@ func (ub *UserBalance) executeActionTriggers(a *Action) {
}
}
} else {
if strings.Contains(at.ThresholdType, "MAX") {
if strings.Contains(at.ThresholdType, "*max") {
if b.Value >= at.ThresholdValue {
// run the actions
at.Execute(ub)

View File

@@ -36,13 +36,13 @@ 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: CREDIT, Direction: OUTBOUND, Units: 10},
&Action{ActionType: "*topup", BalanceId: MINUTES, Direction: OUTBOUND, MinuteBucket: &MinuteBucket{Weight: 20, Price: 1, Seconds: 10, DestinationId: "NAT"}},
}
storageGetter.SetActions("TEST_ACTIONS", ats)
ats1 := []*Action{
&Action{ActionType: "TOPUP", BalanceId: CREDIT, Direction: OUTBOUND, Units: 10, Weight: 20},
&Action{ActionType: "RESET_PREPAID", Weight: 10},
&Action{ActionType: "*topup", BalanceId: CREDIT, Direction: OUTBOUND, Units: 10, Weight: 20},
&Action{ActionType: "*reset_prepaid", Weight: 10},
}
storageGetter.SetActions("TEST_ACTIONS_ORDER", ats1)
}
@@ -423,7 +423,7 @@ func TestUserBalanceExecuteTriggeredActions(t *testing.T) {
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
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: ABSOLUTE, DestinationId: "RET"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 2, ThresholdType: "*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 {
@@ -448,7 +448,7 @@ func TestUserBalanceExecuteTriggeredActionsBalance(t *testing.T) {
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
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: ABSOLUTE, DestinationId: "RET"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: "MIN_COUNTER", ActionsId: "TEST_ACTIONS"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, Direction: OUTBOUND, ThresholdValue: 100, ThresholdType: "*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 {
@@ -461,7 +461,7 @@ func TestUserBalanceExecuteTriggeredActionsOrder(t *testing.T) {
Id: "TEST_UB_OREDER",
BalanceMap: map[string]BalanceChain{CREDIT + OUTBOUND: BalanceChain{&Balance{Value: 100}}},
UnitCounters: []*UnitsCounter{&UnitsCounter{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ThresholdType: "MAX_COUNTER", ActionsId: "TEST_ACTIONS_ORDER"}},
ActionTriggers: ActionTriggerPriotityList{&ActionTrigger{BalanceId: CREDIT, ThresholdValue: 2, ThresholdType: "*max_counter", ActionsId: "TEST_ACTIONS_ORDER"}},
}
ub.countUnits(&Action{BalanceId: CREDIT, Direction: OUTBOUND, Units: 1})
if len(ub.BalanceMap[CREDIT+OUTBOUND]) != 1 || ub.BalanceMap[CREDIT+OUTBOUND][0].Value != 10 {