diff --git a/apier/v1/apier.go b/apier/v1/apier.go index 7120dc1da..169f3a7ea 100644 --- a/apier/v1/apier.go +++ b/apier/v1/apier.go @@ -21,7 +21,6 @@ package apier import ( "errors" "fmt" - "github.com/cgrates/cgrates/config" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/scheduler" @@ -42,17 +41,20 @@ type ApierV1 struct { Config *config.CGRConfig } -type AttrDestination struct { - Id string - Prefixes []string -} - -func (self *ApierV1) GetDestination(attr *AttrDestination, reply *AttrDestination) error { - if dst, err := self.DataDb.GetDestination(attr.Id, false); err != nil { +func (self *ApierV1) GetDestination(dstId string, reply *engine.Destination) error { + if dst, err := self.DataDb.GetDestination(dstId, false); err != nil { return errors.New(utils.ERR_NOT_FOUND) } else { - reply.Id = dst.Id - reply.Prefixes = dst.Prefixes + *reply = *dst + } + return nil +} + +func (self *ApierV1) GetRatingPlan(rplnId string, reply *engine.RatingPlan) error { + if rpln, err := self.DataDb.GetRatingPlan(rplnId, false); err != nil { + return errors.New(utils.ERR_NOT_FOUND) + } else { + *reply = *rpln } return nil } @@ -93,7 +95,6 @@ type AttrAddBalance struct { } func (self *ApierV1) AddBalance(attr *AttrAddBalance, reply *string) error { - // what storage instance do we use? tag := fmt.Sprintf("%s:%s:%s", attr.Direction, attr.Tenant, attr.Account) if _, err := self.DataDb.GetUserBalance(tag); err != nil { diff --git a/apier/v1/apier_local_test.go b/apier/v1/apier_local_test.go index b6f9e51e6..97dbcee5f 100644 --- a/apier/v1/apier_local_test.go +++ b/apier/v1/apier_local_test.go @@ -56,12 +56,16 @@ var testLocal = flag.Bool("local", false, "Perform the tests only on local test var dataDir = flag.String("data_dir", "/usr/share/cgrates/data", "CGR data dir path here") var waitRater = flag.Int("wait_rater", 300, "Number of miliseconds to wait for rater to start and cache") +func init() { + cfgPath := path.Join(*dataDir, "conf", "cgrates.cfg") + cfg, _ = config.NewCGRConfig(&cfgPath) +} + // Empty tables before using them func TestCreateTables(t *testing.T) { if !*testLocal { return } - cfg, _ = config.NewDefaultCGRConfig() var mysql *engine.MySQLStorage if d, err := engine.NewMySQLStorage(cfg.StorDBHost, cfg.StorDBPort, cfg.StorDBName, cfg.StorDBUser, cfg.StorDBPass); err != nil { t.Fatal("Error on opening database connection: ", err) @@ -81,6 +85,19 @@ func TestCreateTables(t *testing.T) { } } +func TestInitDataDb(t *testing.T) { + if !*testLocal { + return + } + dataDb, err := engine.ConfigureDataStorage(cfg.DataDBType, cfg.DataDBHost, cfg.DataDBPort, cfg.DataDBName, cfg.DataDBUser, cfg.DataDBPass, cfg.DBDataEncoding) + if err != nil { + t.Fatal("Cannot connect to dataDb", err) + } + if err := dataDb.Flush(); err != nil { + t.Fatal("Cannot reset dataDb", err) + } +} + // Finds cgr-engine executable and starts it with default configuration func TestStartEngine(t *testing.T) { if !*testLocal { @@ -299,10 +316,13 @@ func TestApierTPDestinationRate(t *testing.T) { dr := &utils.TPDestinationRate{TPid: engine.TEST_SQL, DestinationRateId: "DR_FREESWITCH_USERS", DestinationRates: []*utils.DestinationRate{ &utils.DestinationRate{DestinationId: "FS_USERS", RateId: "RT_FS_USERS"}, }} + drDe := &utils.TPDestinationRate{TPid: engine.TEST_SQL, DestinationRateId: "DR_FREESWITCH_USERS", DestinationRates: []*utils.DestinationRate{ + &utils.DestinationRate{DestinationId: "GERMANY_MOBILE", RateId: "RT_FS_USERS"}, + }} dr2 := new(utils.TPDestinationRate) *dr2 = *dr dr2.DestinationRateId = engine.TEST_SQL - for _, d := range []*utils.TPDestinationRate{dr, dr2} { + for _, d := range []*utils.TPDestinationRate{dr, dr2, drDe} { if err := rater.Call("ApierV1.SetTPDestinationRate", d, &reply); err != nil { t.Error("Got error on ApierV1.SetTPDestinationRate: ", err.Error()) } else if reply != "OK" { @@ -459,10 +479,9 @@ func TestApierTPActions(t *testing.T) { } reply := "" act := &utils.TPActions{TPid: engine.TEST_SQL, ActionsId: "PREPAID_10", Actions: []*utils.TPAction{ - &utils.TPAction{Identifier: "*call_url", ExtraParameters: "http://localhost:8000", Weight:10}, + &utils.TPAction{Identifier: "*call_url", ExtraParameters: "http://localhost:8000", Weight: 10}, &utils.TPAction{Identifier: "*topup_reset", BalanceType: "*monetary", Direction: "*out", Units: 10, ExpiryTime: "*unlimited", DestinationId: "*any", BalanceWeight: 10, Weight: 10}, - }} actTst := new(utils.TPActions) *actTst = *act @@ -717,21 +736,6 @@ func TestApierSetAccountActions(t *testing.T) { } } -// Test here LoadTariffPlanFromFolder -func TestApierLoadTariffPlanFromFolder(t *testing.T) { - if !*testLocal { - return - } - reply := "" - // Simple test that command is executed without errors - attrs := &AttrLoadTPFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "prepaid1centpsec")} - if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { - t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error()) - } else if reply != "OK" { - t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply) - } -} - // Test here ReloadScheduler func TestApierReloadScheduler(t *testing.T) { if !*testLocal { @@ -761,7 +765,110 @@ func TestApierReloadCache(t *testing.T) { } } -// Test here ReloadCache +// Test here GetDestination +func TestApierGetDestination(t *testing.T) { + if !*testLocal { + return + } + reply := new(engine.Destination) + dstId := "GERMANY_MOBILE" + expectedReply := &engine.Destination{Id: dstId, Prefixes: []string{"+4915", "+4916", "+4917"}} + if err := rater.Call("ApierV1.GetDestination", dstId, reply); err != nil { + t.Error("Got error on ApierV1.GetDestination: ", err.Error()) + } else if !reflect.DeepEqual(expectedReply, reply) { + t.Errorf("Calling ApierV1.GetDestination expected: %v, received: %v", expectedReply, reply) + } +} + +// Test here GetRatingPlan +func TestApierGetRatingPlan(t *testing.T) { + if !*testLocal { + return + } + reply := new(engine.RatingPlan) + rplnId := "RETAIL1" + //riTiming := &engine.RITiming{StartTime: "00:00:00"} + rt := &engine.Rate{GroupIntervalStart: 0, Value: 0, RateIncrement: time.Duration(60)*time.Second, RateUnit: time.Duration(60)*time.Second} + riRate := &engine.RIRate{ConnectFee:0, RoundingMethod: "*up", RoundingDecimals: 0, Rates: []*engine.Rate{rt}} + //{"Id":"RETAIL1","Timings":{"96c78ff5":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""}},"Ratings":{"e41ffcf2":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":0}},"DestinationRates":{"FS_USERS":[{"Timing":"96c78ff5","Rating":"e41ffcf2","Weight":10}],"GERMANY_MOBILE":[{"Timing":"96c78ff5","Rating":"e41ffcf2","Weight":10}]} + if err := rater.Call("ApierV1.GetRatingPlan", rplnId, reply); err != nil { + t.Error("Got error on ApierV1.GetRatingPlan: ", err.Error()) + } + // Check parts of info received since a full one is not possible due to unique map keys inside reply + if reply.Id != rplnId { + t.Error("Unexpected id received", reply.Id) + } + if len(reply.Timings)!= 1 || len(reply.Ratings) != 1 { + t.Error("Unexpected number of items received") + } + //for _, tm := range reply.Timings { // We only get one loop + // if !reflect.DeepEqual(tm, riTiming) { + // t.Errorf("Unexpected timings value: %v, expecting: %v", tm, riTiming) + // } + //} + for _, rating := range reply.Ratings { + if !reflect.DeepEqual( rating, riRate ) { + t.Errorf("Unexpected riRate received: %v", rating) + } + } +} + +// Test here AddBalance +func TestApierAddBalance(t *testing.T) { + if !*testLocal { + return + } + reply := "" + attrs := &AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceId: "*monetary", Direction: "*out", Value: 1.5} + if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.AddBalance: ", err.Error()) + } else if reply != "OK" { + t.Errorf("Calling ApierV1.AddBalance received: %s", reply) + } + attrs = &AttrAddBalance{Tenant: "cgrates.org", Account: "dan", BalanceId: "*monetary", Direction: "*out", Value: 1.5} + if err := rater.Call("ApierV1.AddBalance", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.AddBalance: ", err.Error()) + } else if reply != "OK" { + t.Errorf("Calling ApierV1.AddBalance received: %s", reply) + } +} + +// Test here GetBalance +func TestApierGetBalance(t *testing.T) { + if !*testLocal { + return + } + var reply float64 + attrs := &AttrGetBalance{Tenant: "cgrates.org", Account: "1001", BalanceId: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetBalance", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetBalance: ", err.Error()) + } else if reply != 11.5 { // We expect 11.5 since we have added in the previous test 1.5 + t.Errorf("Calling ApierV1.GetBalance expected: 11.5, received: %f", reply) + } + attrs = &AttrGetBalance{Tenant: "cgrates.org", Account: "dan", BalanceId: "*monetary", Direction: "*out"} + if err := rater.Call("ApierV1.GetBalance", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.GetBalance: ", err.Error()) + } else if reply != 1.5 { + t.Errorf("Calling ApierV1.GetBalance expected: 1.5, received: %f", reply) + } +} + +// Test here LoadTariffPlanFromFolder +func TestApierLoadTariffPlanFromFolder(t *testing.T) { + if !*testLocal { + return + } + reply := "" + // Simple test that command is executed without errors + attrs := &AttrLoadTPFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "prepaid1centpsec")} + if err := rater.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil { + t.Error("Got error on ApierV1.LoadTariffPlanFromFolder: ", err.Error()) + } else if reply != "OK" { + t.Error("Calling ApierV1.LoadTariffPlanFromFolder got reply: ", reply) + } +} + +// Test here ResponderGetCost func TestResponderGetCost(t *testing.T) { if !*testLocal { return diff --git a/console/destination.go b/console/destination.go index 30fb9b0e1..9642cbbb0 100644 --- a/console/destination.go +++ b/console/destination.go @@ -20,7 +20,7 @@ package console import ( "fmt" - "github.com/cgrates/cgrates/apier/v1" + "github.com/cgrates/cgrates/engine" ) func init() { @@ -30,8 +30,8 @@ func init() { // Commander implementation type CmdGetDestination struct { rpcMethod string - rpcParams *apier.AttrDestination - rpcResult *apier.AttrDestination + rpcParams string + rpcResult *engine.Destination } // name should be exec's name @@ -42,7 +42,6 @@ func (self *CmdGetDestination) Usage(name string) string { // set param defaults func (self *CmdGetDestination) defaults() error { self.rpcMethod = "Apier.GetDestination" - self.rpcParams = &apier.AttrDestination{} return nil } @@ -53,7 +52,7 @@ func (self *CmdGetDestination) FromArgs(args []string) error { } // Args look OK, set defaults before going further self.defaults() - self.rpcParams.Id = args[2] + self.rpcParams = args[2] return nil } @@ -66,6 +65,6 @@ func (self *CmdGetDestination) RpcParams() interface{} { } func (self *CmdGetDestination) RpcResult() interface{} { - self.rpcResult = &apier.AttrDestination{} + self.rpcResult = new(engine.Destination) return self.rpcResult } diff --git a/engine/calldesc.go b/engine/calldesc.go index 3dfc9c931..6096fbf33 100644 --- a/engine/calldesc.go +++ b/engine/calldesc.go @@ -19,9 +19,9 @@ along with this program. If not, see package engine import ( + "encoding/json" "errors" "fmt" - "encoding/json" "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/utils" @@ -496,7 +496,7 @@ func (cd *CallDescriptor) Debit() (cc *CallCost, err error) { defer storageGetter.SetUserBalance(userBalance) ub, _ := json.Marshal(userBalance) Logger.Debug(fmt.Sprintf("UserBalance: %s", ub)) - cCost,_ := json.Marshal(cc) + cCost, _ := json.Marshal(cc) Logger.Debug(fmt.Sprintf("CallCost: %s", cCost)) if cc.Cost != 0 || cc.ConnectFee != 0 { userBalance.debitCreditBalance(cc, true) diff --git a/engine/loader_helpers_test.go b/engine/loader_helpers_test.go index fb793466b..6faa92e71 100644 --- a/engine/loader_helpers_test.go +++ b/engine/loader_helpers_test.go @@ -19,12 +19,12 @@ along with this program. If not, see package engine import ( + "bufio" "github.com/cgrates/cgrates/utils" - "strings" - "testing" "io" "reflect" - "bufio" + "strings" + "testing" ) var timingsSample = `#Tag,Years,Months,MonthDays,WeekDays,Time @@ -81,7 +81,6 @@ cgrates.org,1001,*out,PREPAID_10,STANDARD_TRIGGERS DUMMY,INVALID;DATA ` - func TestTimingsValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(timingsSample)) lnValidator := FileValidators[utils.TIMINGS_CSV] @@ -98,7 +97,7 @@ func TestTimingsValidator(t *testing.T) { if valid { t.Error("Validation passed for invalid line", ln) } - case 2,4: + case 2, 4: if !valid { t.Error("Validation did not pass for valid line", ln) } @@ -106,7 +105,6 @@ func TestTimingsValidator(t *testing.T) { } } - func TestDestinationsValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(destsSample)) lnValidator := FileValidators[utils.DESTINATIONS_CSV] @@ -123,7 +121,7 @@ func TestDestinationsValidator(t *testing.T) { if valid { t.Error("Validation passed for invalid line", string(ln)) } - case 2,4: + case 2, 4: if !valid { t.Error("Validation did not pass for valid line", string(ln)) } @@ -131,7 +129,6 @@ func TestDestinationsValidator(t *testing.T) { } } - func TestRatesValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(ratesSample)) lnValidator := FileValidators[utils.RATES_CSV] @@ -156,7 +153,6 @@ func TestRatesValidator(t *testing.T) { } } - func TestDestRatesValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(destRatesSample)) lnValidator := FileValidators[utils.DESTINATION_RATES_CSV] @@ -181,7 +177,6 @@ func TestDestRatesValidator(t *testing.T) { } } - func TestRatingPlansValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(ratingPlansSample)) lnValidator := FileValidators[utils.RATING_PLANS_CSV] @@ -230,7 +225,6 @@ func TestRatingProfilesValidator(t *testing.T) { } } - func TestActionsValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(actionsSample)) lnValidator := FileValidators[utils.ACTIONS_CSV] @@ -247,7 +241,7 @@ func TestActionsValidator(t *testing.T) { if valid { t.Error("Validation passed for invalid line", string(ln)) } - case 2,3,4: + case 2, 3, 4: if !valid { t.Error("Validation did not pass for valid line", string(ln)) } @@ -255,7 +249,6 @@ func TestActionsValidator(t *testing.T) { } } - func TestActionTimingsValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(actionTimingsSample)) lnValidator := FileValidators[utils.ACTION_TIMINGS_CSV] @@ -280,7 +273,6 @@ func TestActionTimingsValidator(t *testing.T) { } } - func TestActionTriggersValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(actionTriggersSample)) lnValidator := FileValidators[utils.ACTION_TRIGGERS_CSV] @@ -297,7 +289,7 @@ func TestActionTriggersValidator(t *testing.T) { if valid { t.Error("Validation passed for invalid line", string(ln)) } - case 2,3,4: + case 2, 3, 4: if !valid { t.Error("Validation did not pass for valid line", string(ln)) } @@ -305,7 +297,6 @@ func TestActionTriggersValidator(t *testing.T) { } } - func TestAccountActionsValidator(t *testing.T) { reader := bufio.NewReader(strings.NewReader(accountActionsSample)) lnValidator := FileValidators[utils.ACCOUNT_ACTIONS_CSV] @@ -329,7 +320,6 @@ func TestAccountActionsValidator(t *testing.T) { } } } - func TestTPCSVFileParser(t *testing.T) { bfRdr := bufio.NewReader(strings.NewReader(ratesSample)) @@ -350,15 +340,13 @@ func TestTPCSVFileParser(t *testing.T) { if err != nil { t.Error(err) } - if !reflect.DeepEqual( record, []string{"RT_1CENT","0","1","1s","1s","0s","*up","2"}) { + if !reflect.DeepEqual(record, []string{"RT_1CENT", "0", "1", "1s", "1s", "0s", "*up", "2"}) { t.Error("Unexpected record extracted", record) } case 3: - if err==nil { + if err == nil { t.Error("Expecting invalid line at row 3") } } } } - - diff --git a/engine/realcalls_test.go b/engine/realcalls_test.go index e63988731..ff92da840 100644 --- a/engine/realcalls_test.go +++ b/engine/realcalls_test.go @@ -19,15 +19,14 @@ along with this program. If not, see package engine import ( - "testing" "encoding/json" + "testing" ) var balance1 string = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","BalanceMap":{"*monetary*out":[{"Uuid":"7fe5d6e740b6edd180b96274b8bd4123","Value":10,"ExpirationDate":"0001-01-01T00:00:00Z","Weight":10,"GroupIds":null,"DestinationId":"*any","RateSubject":""}]},"UnitCounters":null,"ActionTriggers":[{"Id":"120ea04d40af91c580adb0da11554c88","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":2,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"fa217a904059cfd3806239f5ad229f4a","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_balance","ThresholdValue":20,"DestinationId":"","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"f05174b740ab987c802a0a29aa5a2764","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*max_counter","ThresholdValue":15,"DestinationId":"FS_USERS","Weight":10,"ActionsId":"LOG_BALANCE","Executed":false},{"Id":"4d6ebf454048371280100094246163a7","BalanceId":"*monetary","Direction":"*out","ThresholdType":"*min_balance","ThresholdValue":0.1,"DestinationId":"","Weight":10,"ActionsId":"WARN_HTTP","Executed":false}],"Groups":null,"UserIds":null}` var callCost1 string = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":0.6,"ConnectFee":0,"Timespans":[{"TimeStart":"2013-12-03T14:36:48+01:00","TimeEnd":"2013-12-03T14:37:48+01:00","Cost":0.6,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":0,"Rates":[{"GroupIntervalStart":0,"Value":0.6,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}` - func TestDebitBalanceForCall1(t *testing.T) { b1 := new(UserBalance) if err := json.Unmarshal([]byte(balance1), b1); err != nil { @@ -50,7 +49,6 @@ var balanceInsufficient = `{"Id":"*out:192.168.56.66:dan","Type":"*prepaid","Bal var costInsufficient = `{"Direction":"*out","TOR":"call","Tenant":"192.168.56.66","Subject":"dan","Account":"dan","Destination":"+4986517174963","Cost":1,"ConnectFee":3,"Timespans":[{"TimeStart":"2013-12-05T09:52:17+01:00","TimeEnd":"2013-12-05T09:53:17+01:00","Cost":1,"RateInterval":{"Timing":{"Years":[],"Months":[],"MonthDays":[],"WeekDays":[],"StartTime":"00:00:00","EndTime":""},"Rating":{"ConnectFee":3,"Rates":[{"GroupIntervalStart":0,"Value":1,"RateIncrement":60000000000,"RateUnit":60000000000}],"RoundingMethod":"*up","RoundingDecimals":2},"Weight":10},"CallDuration":60000000000,"Increments":null,"MatchedSubject":"*out:192.168.56.66:call:*any","MatchedPrefix":"+49"}]}` - func SomeTestDebitInsufficientBalance(t *testing.T) { b1 := new(UserBalance) if err := json.Unmarshal([]byte(balanceInsufficient), b1); err != nil { diff --git a/engine/storage_map.go b/engine/storage_map.go index 9363afff7..ab045282b 100644 --- a/engine/storage_map.go +++ b/engine/storage_map.go @@ -22,11 +22,11 @@ import ( "errors" "fmt" - "strings" - "time" "github.com/cgrates/cgrates/cache2go" "github.com/cgrates/cgrates/history" "github.com/cgrates/cgrates/utils" + "strings" + "time" ) type MapStorage struct {