diff --git a/apier/v1/accounts.go b/apier/v1/accounts.go index f9c3b53ed..b8702597c 100644 --- a/apier/v1/accounts.go +++ b/apier/v1/accounts.go @@ -168,8 +168,7 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error ub = bal } else { // Not found in db, create it here ub = &engine.Account{ - Id: balanceId, - AllowNegative: attr.AllowNegative, + Id: balanceId, } } @@ -183,6 +182,12 @@ func (self *ApierV1) SetAccount(attr utils.AttrSetAccount, reply *string) error at.AccountIds = append(at.AccountIds, balanceId) } } + if attr.AllowNegative != nil { + ub.AllowNegative = *attr.AllowNegative + } + if attr.Disabled != nil { + ub.Disabled = *attr.Disabled + } // All prepared, save account if err := self.AccountDb.SetAccount(ub); err != nil { return 0, err diff --git a/apier/v1/lcr.go b/apier/v1/lcr.go index 513bc64e0..2b8966ac1 100644 --- a/apier/v1/lcr.go +++ b/apier/v1/lcr.go @@ -68,9 +68,11 @@ func (self *ApierV1) GetLcrSuppliers(lcrReq engine.LcrRequest, suppliers *string if err := self.Responder.GetLCR(&engine.AttrGetLcr{CallDescriptor: cd, Paginator: lcrReq.Paginator}, &lcrQried); err != nil { return utils.NewErrServerError(err) } - if lcrQried.HasErrors() && !lcrReq.IgnoreErrors { + if lcrQried.HasErrors() { lcrQried.LogErrors() - return fmt.Errorf("%s:%s", utils.ErrServerError.Error(), "LCR_ERRORS") + if !lcrReq.IgnoreErrors { + return fmt.Errorf("%s:%s", utils.ErrServerError.Error(), "LCR_COMPUTE_ERRORS") + } } if suppliersStr, err := lcrQried.SuppliersString(); err != nil { return utils.NewErrServerError(err) diff --git a/data/tutorials/fs_evsock/freeswitch/etc/freeswitch_conf.tar.gz b/data/tutorials/fs_evsock/freeswitch/etc/freeswitch_conf.tar.gz index f62125994..310388a91 100644 Binary files a/data/tutorials/fs_evsock/freeswitch/etc/freeswitch_conf.tar.gz and b/data/tutorials/fs_evsock/freeswitch/etc/freeswitch_conf.tar.gz differ diff --git a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg index a61eea930..1c962a6aa 100644 --- a/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg +++ b/data/tutorials/kamevapi/kamailio/etc/kamailio/kamailio.cfg @@ -60,7 +60,7 @@ loadmodule "jsonrpc-s.so" # ----------------- setting module-specific parameters --------------- # ----- mi_fifo params ----- -modparam("mi_fifo", "fifo_name", "/tmp/cgr_kamevapi/kamailio/run/kamailio_fifo") +modparam("mi_fifo", "fifo_name", "/tmp/kamailio_fifo") # ----- tm params ----- modparam("tm", "failure_reply_mode", 3) diff --git a/docs/installation.rst b/docs/installation.rst index 9179ed1d0..56d6c0ea3 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -45,18 +45,20 @@ Database setup ~~~~~~~~~~~~~~ For it's operation CGRateS uses more database types, depending on it's nature, install and configuration being further necessary. + At present we support the following databases: -As DataDB types (rating and accounting subsystems): + - Redis_ -As StorDB (persistent storage for CDRs and tariff plan versions). - +Used as DataDb, optimized for real-time information access. Once installed there should be no special requirements in terms of setup since no schema is necessary. + - MySQL_ +Used as StorDb, optimized for CDR archiving and offline Tariff Plan versioning. Once database is installed, CGRateS database needs to be set-up out of provided scripts (example for the paths set-up by debian package) :: @@ -66,12 +68,13 @@ Once database is installed, CGRateS database needs to be set-up out of provided - PostgreSQL_ +Used as StorDb, optimized for CDR archiving and offline Tariff Plan versioning. Once database is installed, CGRateS database needs to be set-up out of provided scripts (example for the paths set-up by debian package) :: cd /usr/share/cgrates/storage/postgres/ - ./setup_cgr_db.sh root CGRateS.org localhost + ./setup_cgr_db.sh .. _Redis: http://redis.io/ .. _MySQL: http://www.mysql.org/ diff --git a/docs/tut_freeswitch.rst b/docs/tut_freeswitch.rst index e03b9c859..dc54b7bdd 100644 --- a/docs/tut_freeswitch.rst +++ b/docs/tut_freeswitch.rst @@ -1,7 +1,7 @@ -sFreeSWITCH Integration Tutorials +FreeSWITCH Integration Tutorials ================================ -In these tutorials we exemplify few cases of integration between FreeSWITCH_ and **CGRateS**. We start with common steps, installation and postinstall processes then we dive into particular configurations, depending on the case we run. +In these tutorials we exemplify few cases of integration between FreeSWITCH_ and **CGRateS**. We start with common steps, installation and postinstall processes then we dive into particular configurations. .. toctree:: @@ -10,7 +10,6 @@ In these tutorials we exemplify few cases of integration between FreeSWITCH_ and tut_freeswitch_installs tut_cgrates_installs tut_jitsi_installs - tut_freeswitch_csv tut_freeswitch_json tut_cgrates_usage diff --git a/docs/tut_freeswitch_json.rst b/docs/tut_freeswitch_json.rst index 0501bcb69..3d9479b9d 100644 --- a/docs/tut_freeswitch_json.rst +++ b/docs/tut_freeswitch_json.rst @@ -4,7 +4,7 @@ FreeSWITCH_ generating *http-json* CDRs Scenario -------- -- FreeSWITCH with *vanilla* configuration, replacing *mod_cdr_csv* with *mod_json_cdr*. +- FreeSWITCH with *vanilla* configuration adding *mod_json_cdr* for CDR generation. - Modified following users (with configs in *etc/freeswitch/directory/default*): 1001-prepaid, 1002-postpaid, 1003-pseudoprepaid, 1004-rated, 1006-prepaid, 1007-rated. - Have added inside default dialplan CGR own extensions just before routing towards users (*etc/freeswitch/dialplan/default.xml*). @@ -13,8 +13,8 @@ Scenario - **CGRateS** with following components: - CGR-SM started as prepaid controller, with debits taking place at 5s intervals. - - CGR-Mediator component attaching costs to the raw CDRs from FreeSWITCH_ inside CGR StorDB. - - CGR-CDRE exporting mediated CDRs from CGR StorDB (export path: */tmp*). + - CGR-CDRS component receiving raw CDRs from FreeSWITCH, storing them and attaching costs inside CGR StorDB. + - CGR-CDRE exporting processed CDRs from CGR StorDB (export path: */tmp*). - CGR-History component keeping the archive of the rates modifications (path browsable with git client at */tmp/cgr_history*). @@ -23,7 +23,7 @@ Starting FreeSWITCH_ with custom configuration :: - /usr/share/cgrates/tutorials/fs_json/freeswitch/etc/init.d/freeswitch start + /usr/share/cgrates/tutorials/fs_evsock/freeswitch/etc/init.d/freeswitch start To verify that FreeSWITCH_ is running we run the console command: @@ -37,7 +37,7 @@ Starting **CGRateS** with custom configuration :: - /usr/share/cgrates/tutorials/fs_json/cgrates/etc/init.d/cgrates start + /usr/share/cgrates/tutorials/fs_evsock/cgrates/etc/init.d/cgrates start Check that cgrates is running @@ -49,7 +49,7 @@ Check that cgrates is running CDR processing -------------- -At the end of each call FreeSWITCH_ will issue a http post with the CDR. This will reach inside **CGRateS** through the *CDRS* component (close to real-time). Once in-there it will be instantly mediated and it is ready to be exported: +At the end of each call FreeSWITCH_ will issue a http post with the CDR. This will reach inside **CGRateS** through the *CDRS* component (close to real-time). Once in-there it will be instantly rated and it is ready to be exported: :: diff --git a/docs/tut_kamailio.rst b/docs/tut_kamailio.rst new file mode 100644 index 000000000..035305881 --- /dev/null +++ b/docs/tut_kamailio.rst @@ -0,0 +1,18 @@ +Kamailio_ Integration Tutorials +=============================== + +In these tutorials we exemplify few cases of integration between Kamailio_ and CGRateS_. We start with common steps, installation and postinstall processes then we dive into particular configurations, depending on the case we run. + + +.. toctree:: + :maxdepth: 2 + + tut_kamailio_installs + tut_cgrates_installs + tut_jitsi_installs + tut_kamailio_evapi + tut_cgrates_usage + +.. _Kamailio: http://www.kamailio.org/ +.. _CGRateS: http://www.cgrates.org/ + diff --git a/docs/tut_kamailio_evapi.rst b/docs/tut_kamailio_evapi.rst new file mode 100644 index 000000000..04df505ff --- /dev/null +++ b/docs/tut_kamailio_evapi.rst @@ -0,0 +1,59 @@ +Kamailio_ interaction via *evapi* module +========================================= + +Scenario +-------- + + - Kamailio default configuration modified for **CGRateS** interaction. For script maintainability and simplicity we have separated CGRateS specific routes in *kamailio-cgrates.cfg* file which is included in main *kamailio.cfg* via include directive. + + - Considering the following users (with configs hardcoded in the *kamailio.cfg* configuration script and loaded in htable): 1001-prepaid, 1002-postpaid, 1003-pseudoprepaid, 1004-rated, 1005-rated, 1006-prepaid, 1007-prepaid. + +- **CGRateS** with following components: + + - CGR-SM started as translator between Kamailio_ and CGR-Rater for both authorization events as well as accounting ones. + - CGR-CDRS component processing raw CDRs from CGR-SM component and storing them inside CGR StorDB. + - CGR-CDRE exporting rated CDRs from CGR StorDB (export path: */tmp*). + - CGR-History component keeping the archive of the rates modifications (path browsable with git client at */tmp/cgr_history*). + + +Starting Kamailio_ with custom configuration +---------------------------------------------- + +:: + + /usr/share/cgrates/tutorials/kamevapi/kamailio/etc/init.d/kamailio start + +To verify that Kamailio_ is running we run the console command: + +:: + + kamctl moni + + +Starting **CGRateS** with custom configuration +---------------------------------------------- + +:: + + /usr/share/cgrates/tutorials/kamevapi/cgrates/etc/init.d/cgrates start + +Make sure that cgrates is running + +:: + + cgr-console status + + +CDR processing +-------------- + +At the end of each call Kamailio_ will generate an CDR event via *evapi* and this will be directed towards the port configured inside *cgrates.json*. This event will reach inside **CGRateS** through the *SM* component (close to real-time). Once in-there it will be instantly rated and be ready for export. + + +**CGRateS** Usage +----------------- + +Since it is common to most of the tutorials, the example for **CGRateS** usage is provided in a separate page `here `_ + + +.. _Kamailio: http://www.kamailio.org/ diff --git a/docs/tut_kamailio_installs.rst b/docs/tut_kamailio_installs.rst new file mode 100644 index 000000000..ff44d32e1 --- /dev/null +++ b/docs/tut_kamailio_installs.rst @@ -0,0 +1,20 @@ +Software installation +===================== + +As operating system we have choosen Debian Wheezy, since all the software components we use provide packaging for it. + +Kamailio_ +--------- + +We got Kamailio_ installed via following commands: +:: + + apt-key adv --recv-keys --keyserver keyserver.ubuntu.com 0xfb40d3e6508ea4c8 + cd /etc/apt/sources.list.d/ + wget http://apt.itsyscom.com/conf/kamailio.apt.list . + apt-get update + apt-get install kamailio kamailio-extra-modules kamailio-json-modules + +Once installed we proceed with loading the configuration out of specific tutorial cases bellow. + +.. _Kamailio: http://www.kamailio.org/ \ No newline at end of file diff --git a/docs/tut_opensips_event.rst b/docs/tut_opensips_event.rst index ca94318fd..9f7473a35 100644 --- a/docs/tut_opensips_event.rst +++ b/docs/tut_opensips_event.rst @@ -12,8 +12,8 @@ Scenario - **CGRateS** with following components: - CGR-SM started as translator between OpenSIPS_ and **cgr-rater** for both authorization events (pseudoprepaid) as well as CDR ones. - - CGR-Mediator component attaching costs to the raw CDRs from OpenSIPS_ inside CGR StorDB. - - CGR-CDRE exporting mediated CDRs from CGR StorDB (export path: */tmp*). + - CGR-CDRS component processing raw CDRs from CGR-SM component and storing them inside CGR StorDB. + - CGR-CDRE exporting rated CDRs from CGR StorDB (export path: */tmp*). - CGR-History component keeping the archive of the rates modifications (path browsable with git client at */tmp/cgr_history*). @@ -22,7 +22,7 @@ Starting OpenSIPS_ with custom configuration :: - /usr/share/cgrates/tutorials/osips_event/opensips/etc/init.d/opensips start + /usr/share/cgrates/tutorials/osips_async/opensips/etc/init.d/opensips start To verify that OpenSIPS_ is running we run the console command: @@ -36,9 +36,9 @@ Starting **CGRateS** with custom configuration :: - /usr/share/cgrates/tutorials/osips_event/cgrates/etc/init.d/cgrates start + /usr/share/cgrates/tutorials/osips_async/cgrates/etc/init.d/cgrates start -Check that cgrates is running +Make sure that cgrates is running :: @@ -48,7 +48,7 @@ Check that cgrates is running CDR processing -------------- -At the end of each call OpenSIPS_ will generate an CDR event and due to automatic handler registration built in **CGRateS-SM** component, this will be directed towards the port configured inside *cgrates.cfg*. This event will reach inside **CGRateS** through the *SM* component (close to real-time). Once in-there it will be instantly mediated and it is ready to be exported. +At the end of each call OpenSIPS_ will generate an CDR event and due to automatic handler registration built in **CGRateS-SM** component, this will be directed towards the port configured inside *cgrates.json*. This event will reach inside **CGRateS** through the *SM* component (close to real-time). Once in-there it will be instantly rated and be ready for export. **CGRateS** Usage diff --git a/docs/tut_opensips_installs.rst b/docs/tut_opensips_installs.rst index 89aa1a6cc..2080e548e 100644 --- a/docs/tut_opensips_installs.rst +++ b/docs/tut_opensips_installs.rst @@ -12,9 +12,9 @@ We got OpenSIPS_ installed via following commands: wget http://apt.opensips.org/key.asc apt-key add key.asc cd /etc/apt/sources.list.d/ - wget http://apt.itsyscom.com/conf/opensips.apt.list + wget http://apt.itsyscom.com/conf/opensips.wheezy.apt.list apt-get update - apt-get install + apt-get install opensips opensips-json-module opensips-restclient-module Once installed we proceed with loading the configuration out of specific tutorial cases bellow. diff --git a/docs/tutorials.rst b/docs/tutorials.rst index ff937c41e..8e5d72b64 100644 --- a/docs/tutorials.rst +++ b/docs/tutorials.rst @@ -5,4 +5,5 @@ :maxdepth: 2 tut_freeswitch + tut_kamailio tut_opensips diff --git a/engine/lcr.go b/engine/lcr.go index f5925d256..b2df016e3 100644 --- a/engine/lcr.go +++ b/engine/lcr.go @@ -54,7 +54,7 @@ type LcrRequest struct { Account string Subject string Destination string - StartTime string + SetupTime string Duration string IgnoreErrors bool *utils.Paginator @@ -79,9 +79,9 @@ func (self *LcrRequest) AsCallDescriptor() (*CallDescriptor, error) { } var timeStart time.Time var err error - if len(self.StartTime) == 0 { + if len(self.SetupTime) == 0 { timeStart = time.Now() - } else if timeStart, err = utils.ParseTimeDetectLayout(self.StartTime); err != nil { + } else if timeStart, err = utils.ParseTimeDetectLayout(self.SetupTime); err != nil { return nil, err } var callDur time.Duration diff --git a/engine/lcr_test.go b/engine/lcr_test.go index 1bd083d87..79f036087 100644 --- a/engine/lcr_test.go +++ b/engine/lcr_test.go @@ -215,11 +215,11 @@ func TestLcrGet(t *testing.T) { func TestLcrRequestAsCallDescriptor(t *testing.T) { sTime := time.Date(2015, 04, 06, 17, 40, 0, 0, time.UTC) callDur := time.Duration(1) * time.Minute - lcrReq := &LcrRequest{Account: "2001", StartTime: sTime.String()} + lcrReq := &LcrRequest{Account: "2001", SetupTime: sTime.String()} if _, err := lcrReq.AsCallDescriptor(); err == nil || err != utils.ErrMandatoryIeMissing { t.Error("Unexpected error received: %v", err) } - lcrReq = &LcrRequest{Account: "2001", Destination: "2002", StartTime: sTime.String()} + lcrReq = &LcrRequest{Account: "2001", Destination: "2002", SetupTime: sTime.String()} eCd := &CallDescriptor{ Direction: utils.OUT, Tenant: config.CgrConfig().DefaultTenant, diff --git a/general_tests/tutorial_fs_calls_test.go b/general_tests/tutorial_fs_calls_test.go index fbdb3c590..6ae5fa55f 100644 --- a/general_tests/tutorial_fs_calls_test.go +++ b/general_tests/tutorial_fs_calls_test.go @@ -23,6 +23,7 @@ import ( "net/rpc/jsonrpc" "os" "path" + "reflect" "strings" "testing" "time" @@ -94,13 +95,11 @@ func TestTutFsCallsStartEngine(t *testing.T) { } // Restart FS so we make sure reconnects are working - func TestTutFsCallsRestartFS(t *testing.T) { if !*testCalls { return } - engine.KillProcName("freeswitch", 5000) - if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "freeswitch", "etc", "init.d", "freeswitch"), "start", 3000); err != nil { + if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "freeswitch", "etc", "init.d", "freeswitch"), "restart", 5000); err != nil { t.Fatal(err) } } @@ -174,6 +173,51 @@ func TestTutFsCallsAccountsBefore(t *testing.T) { } } +// Make sure all stats queues are in place +func TestTutFsCallsCdrStatsBefore(t *testing.T) { + if !*testCalls { + return + } + //eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"} + var statMetrics map[string]float64 + eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1} + if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutFsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } +} + // Start Pjsua as listener and register it to receive calls func TestTutFsCallsStartPjsuaListener(t *testing.T) { if !*testCalls { @@ -181,12 +225,12 @@ func TestTutFsCallsStartPjsuaListener(t *testing.T) { } var err error acnts := []*engine.PjsuaAccount{ - &engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:25060"}, - &engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:25060"}, - &engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:25060"}, - &engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:25060"}, - &engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:25060"}, - &engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:25060"}} + &engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}} if tutFsCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, *waitRater); err != nil { t.Fatal(err) } @@ -198,7 +242,7 @@ func TestTutFsCallsCall1001To1002(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(67)*time.Second, 5071); err != nil { + "sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil { t.Fatal(err) } } @@ -209,7 +253,7 @@ func TestTutFsCallsCall1001To1003(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1003@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(65)*time.Second, 5072); err != nil { + "sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil { t.Fatal(err) } } @@ -219,7 +263,7 @@ func TestTutFsCallsCall1002To1001(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(61)*time.Second, 5073); err != nil { + "sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil { t.Fatal(err) } } @@ -229,7 +273,7 @@ func TestTutFsCallsCall1003To1001(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(63)*time.Second, 5074); err != nil { + "sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil { t.Fatal(err) } } @@ -239,7 +283,7 @@ func TestTutFsCallsCall1004To1001(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(62)*time.Second, 5075); err != nil { + "sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil { t.Fatal(err) } } @@ -249,7 +293,7 @@ func TestTutFsCallsCall1006To1002(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(64)*time.Second, 5076); err != nil { + "sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil { t.Fatal(err) } } @@ -259,7 +303,7 @@ func TestTutFsCallsCall1007To1002(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:25060", time.Duration(66)*time.Second, 5077); err != nil { + "sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil { t.Fatal(err) } } @@ -282,7 +326,7 @@ func TestTutFsCallsAccount1001(t *testing.T) { } // Make sure account was debited properly -func TestTutFsCallsCdrs(t *testing.T) { +func TestTutFsCalls1001Cdrs(t *testing.T) { if !*testCalls { return } @@ -308,6 +352,9 @@ func TestTutFsCallsCdrs(t *testing.T) { if reply[0].Cost == -1.0 { // Cost was not calculated t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) } + //if reply[0].Supplier != "suppl2" { // Usage as seconds + // t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) + //} } // Make sure call cost contains the matched information if err := tutFsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil { @@ -352,7 +399,16 @@ func TestTutFsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Subject for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1002"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutFsCalls1002Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1002"}, RunIds: []string{utils.META_DEFAULT}} if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -371,7 +427,15 @@ func TestTutFsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1003"}, RunIds: []string{utils.META_DEFAULT}} +} + +// Make sure account was debited properly +func TestTutFsCalls1003Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1003"}, RunIds: []string{utils.META_DEFAULT}} if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -390,7 +454,16 @@ func TestTutFsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1004"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutFsCalls1004Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1004"}, RunIds: []string{utils.META_DEFAULT}} if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -409,7 +482,16 @@ func TestTutFsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1006"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutFsCalls1006Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1006"}, RunIds: []string{utils.META_DEFAULT}} if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -431,7 +513,15 @@ func TestTutFsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1007"}, RunIds: []string{utils.META_DEFAULT}} +} + +// Make sure account was debited properly +func TestTutFsCalls1007Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1007"}, RunIds: []string{utils.META_DEFAULT}} if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { diff --git a/general_tests/tutorial_kam_calls_test.go b/general_tests/tutorial_kam_calls_test.go index a4dba99b3..12ce0cfb9 100644 --- a/general_tests/tutorial_kam_calls_test.go +++ b/general_tests/tutorial_kam_calls_test.go @@ -72,13 +72,13 @@ func TestTutKamCallsResetStorDb(t *testing.T) { } } -// start Kam server -func TestTutKamCallsStartKam(t *testing.T) { +// start FS server +func TestTutKamCallsStartKamailio(t *testing.T) { if !*testCalls { return } - engine.KillProcName("kamailio", *waitRater) - if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "start", 3000); err != nil { + engine.KillProcName("kamailio", 3000) + if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "start", 2000); err != nil { t.Fatal(err) } } @@ -94,12 +94,12 @@ func TestTutKamCallsStartEngine(t *testing.T) { } } -// Restart Kam so we make sure reconnects are working -func TestTutKamCallsRestartKam(t *testing.T) { +// Restart FS so we make sure reconnects are working +func TestTutKamCallsRestartKamailio(t *testing.T) { if !*testCalls { return } - if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "restart", 4000); err != nil { + if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "restart", 3000); err != nil { t.Fatal(err) } } @@ -131,7 +131,7 @@ func TestTutKamCallsLoadTariffPlanFromFolder(t *testing.T) { time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups } -// Make sure account was topped-up properly +// Make sure account was debited properly func TestTutKamCallsAccountsBefore(t *testing.T) { if !*testCalls { return @@ -180,13 +180,7 @@ func TestTutKamCallsCdrStatsBefore(t *testing.T) { } //eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"} var statMetrics map[string]float64 - eMetrics := map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: utils.META_DEFAULT}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil { t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) } else if !reflect.DeepEqual(eMetrics, statMetrics) { @@ -253,12 +247,23 @@ func TestTutKamCallsCall1001To1002(t *testing.T) { } } +// Call from 1001 (prepaid) to 1003 +func TestTutKamCallsCall1001To1003(t *testing.T) { + if !*testCalls { + return + } + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*"}, "sip:1003@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil { + t.Fatal(err) + } +} + func TestTutKamCallsCall1002To1001(t *testing.T) { if !*testCalls { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5072); err != nil { + "sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil { t.Fatal(err) } } @@ -268,7 +273,7 @@ func TestTutKamCallsCall1003To1001(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5073); err != nil { + "sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil { t.Fatal(err) } } @@ -278,7 +283,7 @@ func TestTutKamCallsCall1004To1001(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5074); err != nil { + "sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil { t.Fatal(err) } } @@ -288,7 +293,7 @@ func TestTutKamCallsCall1006To1002(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5075); err != nil { + "sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil { t.Fatal(err) } } @@ -298,42 +303,7 @@ func TestTutKamCallsCall1007To1002(t *testing.T) { return } if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5076); err != nil { - t.Fatal(err) - } -} - -/* -// Should hangup at 62 seconds, disconnect from SM -func TestTutKamCallsCall1007To1007(t *testing.T) { - if !*testCalls { - return - } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(75)*time.Second, 5077); err != nil { - t.Fatal(err) - } -} - -// Should hangup at 62 seconds, disconnect from Kamailio -func TestTutKamCallsCall1003To1007(t *testing.T) { - if !*testCalls { - return - } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(73)*time.Second, 5078); err != nil { - t.Fatal(err) - } -} -*/ - -// Call from 1001 (prepaid) to 1007, should not cost more than 62 which is MaxCallCost -func TestTutKamCallsCall1001To1007(t *testing.T) { - if !*testCalls { - return - } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*"}, "sip:1007@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(70)*time.Second, 5079); err != nil { + "sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil { t.Fatal(err) } } @@ -343,7 +313,7 @@ func TestTutKamCallsAccount1001(t *testing.T) { if !*testCalls { return } - time.Sleep(time.Duration(80) * time.Second) // Allow calls to finish before start querying the results + time.Sleep(time.Duration(70) * time.Second) // Allow calls to finish before start querying the results var reply *engine.Account attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001", Direction: "*out"} if err := tutKamCallsRpc.Call("ApierV1.GetAccount", attrs, &reply); err != nil { @@ -356,17 +326,20 @@ func TestTutKamCallsAccount1001(t *testing.T) { } // Make sure account was debited properly -func TestTutKamCallsCdrs(t *testing.T) { +func TestTutKamCalls1001Cdrs(t *testing.T) { if !*testCalls { return } var reply []*engine.ExternalCdr - req := utils.RpcCdrsFilter{Accounts: []string{"1001"}, RunIds: []string{utils.META_DEFAULT}} + var cgrId string // Share with getCostDetails + var cCost engine.CallCost + req := utils.RpcCdrsFilter{RunIds: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestPrefixes: []string{"1002"}} if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 2 { + } else if len(reply) != 1 { t.Error("Unexpected number of CDRs returned: ", len(reply)) } else { + cgrId = reply[0].CgrId if reply[0].CdrSource != "KAMAILIO_CGR_CALL_END" { t.Errorf("Unexpected CdrSource for CDR: %+v", reply[0]) } @@ -374,11 +347,44 @@ func TestTutKamCallsCdrs(t *testing.T) { t.Errorf("Unexpected ReqType for CDR: %+v", reply[0]) } if reply[0].Usage != "67" { // Usage as seconds - t.Errorf("Unexpected Usage for 428CDR: %+v", reply[0]) + t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } - if reply[0].Supplier != "suppl2" { // Usage as seconds - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) + if reply[0].Cost == -1.0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) } + //if reply[0].Supplier != "suppl2" { // Usage as seconds + // t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) + //} + } + // Make sure call cost contains the matched information + if err := tutKamCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") { + t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0]) + } + + req = utils.RpcCdrsFilter{RunIds: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestPrefixes: []string{"1003"}} + if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(reply)) + } else { + cgrId = reply[0].CgrId + if reply[0].ReqType != utils.META_PREPAID { + t.Errorf("Unexpected ReqType for CDR: %+v", reply[0]) + } + if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds + t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) + } + if reply[0].Cost != 0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) + } + } + // Make sure call cost contains the matched information + if err := tutKamCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") { + t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0]) } req = utils.RpcCdrsFilter{Accounts: []string{"1001"}, RunIds: []string{"derived_run1"}, FilterOnRated: true} if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { @@ -392,11 +398,17 @@ func TestTutKamCallsCdrs(t *testing.T) { if reply[0].Subject != "1002" { t.Errorf("Unexpected Subject for CDR: %+v", reply[0]) } - if reply[0].Supplier != "suppl2" { - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) - } } - req = utils.RpcCdrsFilter{Accounts: []string{"1002"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutKamCalls1002Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1002"}, RunIds: []string{utils.META_DEFAULT}} if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -414,11 +426,16 @@ func TestTutKamCallsCdrs(t *testing.T) { if reply[0].Usage != "61" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } - if reply[0].Supplier != "suppl1" { - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) - } } - req = utils.RpcCdrsFilter{Accounts: []string{"1003"}, RunIds: []string{utils.META_DEFAULT}} +} + +// Make sure account was debited properly +func TestTutKamCalls1003Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1003"}, RunIds: []string{utils.META_DEFAULT}} if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -433,14 +450,20 @@ func TestTutKamCallsCdrs(t *testing.T) { if reply[0].Destination != "1001" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "63" { // Usage as seconds + if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } - if reply[0].Supplier != "suppl1" { - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) - } } - req = utils.RpcCdrsFilter{Accounts: []string{"1004"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutKamCalls1004Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1004"}, RunIds: []string{utils.META_DEFAULT}} if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -455,14 +478,20 @@ func TestTutKamCallsCdrs(t *testing.T) { if reply[0].Destination != "1001" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "62" { // Usage as seconds + if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } - if reply[0].Supplier != "suppl1" { - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) - } } - req = utils.RpcCdrsFilter{Accounts: []string{"1006"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutKamCalls1006Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1006"}, RunIds: []string{utils.META_DEFAULT}} if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -477,84 +506,42 @@ func TestTutKamCallsCdrs(t *testing.T) { if reply[0].Destination != "1002" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "64" { // Usage as seconds + if reply[0].Usage != "64" && reply[0].Usage != "65" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } - if reply[0].Supplier != "suppl3" { - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) - } - } - req = utils.RpcCdrsFilter{Accounts: []string{"1007"}, RunIds: []string{utils.META_DEFAULT}} - if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { - t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 1 { - t.Error("Unexpected number of CDRs returned: ", len(reply)) - } else { - if reply[0].CdrSource != "KAMAILIO_CGR_CALL_END" { - t.Errorf("Unexpected CdrSource for CDR: %+v", reply[0]) - } - if reply[0].ReqType != utils.META_PREPAID { - t.Errorf("Unexpected ReqType for CDR: %+v", reply[0]) - } - if reply[0].Destination != "1002" { - t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) - } - if reply[0].Usage != "66" { // Usage as seconds - t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) - } - if reply[0].Supplier != "suppl3" { - t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) + if reply[0].Cost == -1.0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) } } } -// Make sure all stats queues were updated -func TestTutKamCallsCdrStatsAfter(t *testing.T) { +// Make sure account was debited properly +func TestTutKamCalls1007Cdrs(t *testing.T) { if !*testCalls { return } - var statMetrics map[string]float64 - eMetrics := map[string]float64{engine.ACC: 0.9015222222, engine.ACD: 65.5555555556, engine.ASR: 100} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: utils.META_DEFAULT}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.ACC: 0.8829, engine.ACD: 64.7142857143, engine.ASR: 100, engine.TCC: 6.1803, engine.TCD: 453} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.TCC: 6.1803, engine.TCD: 453, engine.ACC: 0.32, engine.ACD: 68.5, engine.ASR: 100} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.ACD: 61, engine.ASR: 100, engine.TCC: 6.1803, engine.TCD: 453, engine.ACC: 1.2334} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.TCC: 6.1803, engine.TCD: 453, engine.ACC: 1.2334, engine.ACD: -1, engine.ASR: -1} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.ACC: 1.00404, engine.ACD: 65.2, engine.ASR: 100, engine.TCC: 5.0202, engine.TCD: 326} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) - } - eMetrics = map[string]float64{engine.ACD: 67, engine.ASR: 100, engine.TCC: 1.2534, engine.TCD: 134, engine.ACC: 0.6267} - if err := tutKamCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil { - t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) - } else if !reflect.DeepEqual(eMetrics, statMetrics) { - t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1007"}, RunIds: []string{utils.META_DEFAULT}} + if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(reply)) + } else { + if reply[0].CdrSource != "KAMAILIO_CGR_CALL_END" { + t.Errorf("Unexpected CdrSource for CDR: %+v", reply[0]) + } + if reply[0].ReqType != utils.META_PREPAID { + t.Errorf("Unexpected ReqType for CDR: %+v", reply[0]) + } + if reply[0].Destination != "1002" { + t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) + } + if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds + t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) + } + if reply[0].Cost == -1.0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) + } } } diff --git a/general_tests/tutorial_osips_calls_test.go b/general_tests/tutorial_osips_calls_test.go index 31b8b0b60..25fa947de 100644 --- a/general_tests/tutorial_osips_calls_test.go +++ b/general_tests/tutorial_osips_calls_test.go @@ -23,6 +23,7 @@ import ( "net/rpc/jsonrpc" "os" "path" + "reflect" "strings" "testing" "time" @@ -71,13 +72,13 @@ func TestTutOsipsCallsResetStorDb(t *testing.T) { } } -// start Kam server +// start FS server func TestTutOsipsCallsStartOsips(t *testing.T) { if !*testCalls { return } - engine.KillProcName("opensips", *waitRater) - if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "start", 100); err != nil { + engine.KillProcName("opensips", 3000) + if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "start", 3000); err != nil { t.Fatal(err) } } @@ -93,12 +94,12 @@ func TestTutOsipsCallsStartEngine(t *testing.T) { } } -// Restart Kam so we make sure reconnects are working -func TestTutOsipsCallsRestartKam(t *testing.T) { +// Restart FS so we make sure reconnects are working +func TestTutOsipsCallsRestartOsips(t *testing.T) { if !*testCalls { return } - if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "restart", 200); err != nil { + if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "restart", 3000); err != nil { t.Fatal(err) } } @@ -172,6 +173,51 @@ func TestTutOsipsCallsAccountsBefore(t *testing.T) { } } +// Make sure all stats queues are in place +func TestTutOsipsCallsCdrStatsBefore(t *testing.T) { + if !*testCalls { + return + } + //eQueueIds := []string{"*default", "CDRST1", "CDRST_1001", "CDRST_1002", "CDRST_1003", "STATS_SUPPL1", "STATS_SUPPL2"} + var statMetrics map[string]float64 + eMetrics := map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST1"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACC: -1, engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1} + if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1001"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1002"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "CDRST_1003"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL1"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } + eMetrics = map[string]float64{engine.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1} + if err := tutOsipsCallsRpc.Call("CDRStatsV1.GetMetrics", v1.AttrGetMetrics{StatsQueueId: "STATS_SUPPL2"}, &statMetrics); err != nil { + t.Error("Calling CDRStatsV1.GetMetrics, got error: ", err.Error()) + } else if !reflect.DeepEqual(eMetrics, statMetrics) { + t.Errorf("Expecting: %v, received: %v", eMetrics, statMetrics) + } +} + // Start Pjsua as listener and register it to receive calls func TestTutOsipsCallsStartPjsuaListener(t *testing.T) { if !*testCalls { @@ -179,12 +225,12 @@ func TestTutOsipsCallsStartPjsuaListener(t *testing.T) { } var err error acnts := []*engine.PjsuaAccount{ - &engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, - &engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, - &engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, - &engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, - &engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, - &engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5060"}} + &engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}, + &engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*", Registrar: "sip:127.0.0.1:5060"}} if tutOsipsCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, *waitRater); err != nil { t.Fatal(err) } @@ -195,18 +241,29 @@ func TestTutOsipsCallsCall1001To1002(t *testing.T) { if !*testCalls { return } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1", + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1", "sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil { t.Fatal(err) } } +// Call from 1001 (prepaid) to 1003 +func TestTutOsipsCallsCall1001To1003(t *testing.T) { + if !*testCalls { + return + } + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: "1234", Realm: "*"}, "sip:1003@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(65)*time.Second, 5072); err != nil { + t.Fatal(err) + } +} + func TestTutOsipsCallsCall1002To1001(t *testing.T) { if !*testCalls { return } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5072); err != nil { + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil { t.Fatal(err) } } @@ -215,8 +272,8 @@ func TestTutOsipsCallsCall1003To1001(t *testing.T) { if !*testCalls { return } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5073); err != nil { + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil { t.Fatal(err) } } @@ -225,8 +282,8 @@ func TestTutOsipsCallsCall1004To1001(t *testing.T) { if !*testCalls { return } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "CGRateS.org", Realm: "*"}, "sip:1001@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5074); err != nil { + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: "1234", Realm: "*"}, "sip:1001@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil { t.Fatal(err) } } @@ -235,8 +292,8 @@ func TestTutOsipsCallsCall1006To1002(t *testing.T) { if !*testCalls { return } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5075); err != nil { + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil { t.Fatal(err) } } @@ -245,8 +302,8 @@ func TestTutOsipsCallsCall1007To1002(t *testing.T) { if !*testCalls { return } - if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "CGRateS.org", Realm: "*"}, "sip:1002@127.0.0.1", - "sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5076); err != nil { + if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: "1234", Realm: "*"}, "sip:1002@127.0.0.1", + "sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil { t.Fatal(err) } } @@ -269,17 +326,20 @@ func TestTutOsipsCallsAccount1001(t *testing.T) { } // Make sure account was debited properly -func TestTutOsipsCallsCdrs(t *testing.T) { +func TestTutOsipsCalls1001Cdrs(t *testing.T) { if !*testCalls { return } var reply []*engine.ExternalCdr - req := utils.RpcCdrsFilter{Accounts: []string{"1001"}, RunIds: []string{utils.META_DEFAULT}} + var cgrId string // Share with getCostDetails + var cCost engine.CallCost + req := utils.RpcCdrsFilter{RunIds: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestPrefixes: []string{"1002"}} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { t.Error("Unexpected number of CDRs returned: ", len(reply)) } else { + cgrId = reply[0].CgrId if reply[0].CdrSource != "OSIPS_E_ACC_EVENT" { t.Errorf("Unexpected CdrSource for CDR: %+v", reply[0]) } @@ -289,11 +349,47 @@ func TestTutOsipsCallsCdrs(t *testing.T) { if reply[0].Usage != "67" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } + if reply[0].Cost == -1.0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) + } + //if reply[0].Supplier != "suppl2" { // Usage as seconds + // t.Errorf("Unexpected Supplier for CDR: %+v", reply[0]) + //} + } + // Make sure call cost contains the matched information + if err := tutOsipsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") { + t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0]) + } + + req = utils.RpcCdrsFilter{RunIds: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestPrefixes: []string{"1003"}} + if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if len(reply) != 1 { + t.Error("Unexpected number of CDRs returned: ", len(reply)) + } else { + cgrId = reply[0].CgrId + if reply[0].ReqType != utils.META_PREPAID { + t.Errorf("Unexpected ReqType for CDR: %+v", reply[0]) + } + if reply[0].Usage != "65" && reply[0].Usage != "66" { // Usage as seconds + t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) + } + if reply[0].Cost != 0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) + } + } + // Make sure call cost contains the matched information + if err := tutOsipsCallsRpc.Call("ApierV2.GetCallCostLog", utils.AttrGetCallCost{CgrId: cgrId}, &cCost); err != nil { + t.Error("Unexpected error: ", err.Error()) + } else if utils.IsSliceMember([]string{cCost.Timespans[0].MatchedSubject, cCost.Timespans[0].MatchedPrefix, cCost.Timespans[0].MatchedDestId}, "") { + t.Errorf("Unexpected Matched* for CallCost: %+v", cCost.Timespans[0]) } req = utils.RpcCdrsFilter{Accounts: []string{"1001"}, RunIds: []string{"derived_run1"}, FilterOnRated: true} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) - } else if len(reply) != 1 { + } else if len(reply) != 2 { t.Error("Unexpected number of CDRs returned: ", len(reply)) } else { if reply[0].ReqType != utils.META_RATED { @@ -303,7 +399,16 @@ func TestTutOsipsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Subject for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1002"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutOsipsCalls1002Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1002"}, RunIds: []string{utils.META_DEFAULT}} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -322,7 +427,15 @@ func TestTutOsipsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1003"}, RunIds: []string{utils.META_DEFAULT}} +} + +// Make sure account was debited properly +func TestTutOsipsCalls1003Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1003"}, RunIds: []string{utils.META_DEFAULT}} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -337,11 +450,20 @@ func TestTutOsipsCallsCdrs(t *testing.T) { if reply[0].Destination != "1001" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "63" { // Usage as seconds + if reply[0].Usage != "63" && reply[0].Usage != "64" { // Usage as seconds, sometimes takes a second longer to disconnect t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1004"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutOsipsCalls1004Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1004"}, RunIds: []string{utils.META_DEFAULT}} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -360,7 +482,16 @@ func TestTutOsipsCallsCdrs(t *testing.T) { t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } } - req = utils.RpcCdrsFilter{Accounts: []string{"1006"}, RunIds: []string{utils.META_DEFAULT}} + +} + +// Make sure account was debited properly +func TestTutOsipsCalls1006Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1006"}, RunIds: []string{utils.META_DEFAULT}} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -375,11 +506,22 @@ func TestTutOsipsCallsCdrs(t *testing.T) { if reply[0].Destination != "1002" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "64" { // Usage as seconds + if reply[0].Usage != "64" && reply[0].Usage != "65" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } + if reply[0].Cost == -1.0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) + } } - req = utils.RpcCdrsFilter{Accounts: []string{"1007"}, RunIds: []string{utils.META_DEFAULT}} +} + +// Make sure account was debited properly +func TestTutOsipsCalls1007Cdrs(t *testing.T) { + if !*testCalls { + return + } + var reply []*engine.ExternalCdr + req := utils.RpcCdrsFilter{Accounts: []string{"1007"}, RunIds: []string{utils.META_DEFAULT}} if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil { t.Error("Unexpected error: ", err.Error()) } else if len(reply) != 1 { @@ -394,9 +536,12 @@ func TestTutOsipsCallsCdrs(t *testing.T) { if reply[0].Destination != "1002" { t.Errorf("Unexpected Destination for CDR: %+v", reply[0]) } - if reply[0].Usage != "66" { // Usage as seconds + if reply[0].Usage != "66" && reply[0].Usage != "67" { // Usage as seconds t.Errorf("Unexpected Usage for CDR: %+v", reply[0]) } + if reply[0].Cost == -1.0 { // Cost was not calculated + t.Errorf("Unexpected Cost for CDR: %+v", reply[0]) + } } } diff --git a/sessionmanager/fsevent.go b/sessionmanager/fsevent.go index 914c6e806..2e1273aec 100644 --- a/sessionmanager/fsevent.go +++ b/sessionmanager/fsevent.go @@ -386,7 +386,7 @@ func (fsev FSEvent) AsCallDescriptor() (*engine.CallDescriptor, error) { Account: fsev.GetAccount(utils.META_DEFAULT), Subject: fsev.GetSubject(utils.META_DEFAULT), Destination: fsev.GetDestination(utils.META_DEFAULT), - StartTime: utils.FirstNonEmpty(fsev[SETUP_TIME], fsev[ANSWER_TIME]), + SetupTime: utils.FirstNonEmpty(fsev[SETUP_TIME], fsev[ANSWER_TIME]), Duration: fsev[DURATION], } return lcrReq.AsCallDescriptor() diff --git a/sessionmanager/kamevent.go b/sessionmanager/kamevent.go index 2ec757dcf..07669b43e 100644 --- a/sessionmanager/kamevent.go +++ b/sessionmanager/kamevent.go @@ -381,7 +381,7 @@ func (kev KamEvent) AsCallDescriptor() (*engine.CallDescriptor, error) { Account: kev.GetAccount(utils.META_DEFAULT), Subject: kev.GetSubject(utils.META_DEFAULT), Destination: kev.GetDestination(utils.META_DEFAULT), - StartTime: utils.FirstNonEmpty(kev[CGR_SETUPTIME], kev[CGR_ANSWERTIME]), + SetupTime: utils.FirstNonEmpty(kev[CGR_SETUPTIME], kev[CGR_ANSWERTIME]), Duration: kev[CGR_DURATION], } return lcrReq.AsCallDescriptor() diff --git a/utils/apitpdata.go b/utils/apitpdata.go index afecaf679..03cc03d36 100644 --- a/utils/apitpdata.go +++ b/utils/apitpdata.go @@ -1078,7 +1078,8 @@ type AttrSetAccount struct { Direction string Account string ActionPlanId string - AllowNegative bool + AllowNegative *bool + Disabled *bool } type AttrRemoveAccount struct {