Update Asterisk Agent and add it in /general_tests/tutorial_calls_test.go

This commit is contained in:
TeoV
2018-05-15 08:41:35 -04:00
committed by Dan Christian Bogos
parent 64426affc4
commit 94fbf5650b
12 changed files with 131 additions and 2394 deletions

View File

@@ -175,85 +175,12 @@ func (smaEv *SMAsteriskEvent) ExtraParameters() (extraParams map[string]string)
return
}
func (smaEv *SMAsteriskEvent) AsSMGenericEvent() *sessions.SMGenericEvent {
var evName string
switch smaEv.EventType() {
case ARIStasisStart:
evName = SMAAuthorization
case ARIChannelStateChange:
evName = SMASessionStart
case ARIChannelDestroyed:
evName = SMASessionTerminate
}
smgEv := sessions.SMGenericEvent{utils.EVENT_NAME: evName}
smgEv[utils.OriginID] = smaEv.ChannelID()
if smaEv.RequestType() != "" {
smgEv[utils.RequestType] = smaEv.RequestType()
}
if smaEv.Tenant() != "" {
smgEv[utils.Tenant] = smaEv.Tenant()
}
if smaEv.Category() != "" {
smgEv[utils.Category] = smaEv.Category()
}
if smaEv.Subject() != "" {
smgEv[utils.Subject] = smaEv.Subject()
}
smgEv[utils.OriginHost] = smaEv.OriginatorIP()
smgEv[utils.Account] = smaEv.Account()
smgEv[utils.Destination] = smaEv.Destination()
smgEv[utils.SetupTime] = smaEv.Timestamp()
if smaEv.Supplier() != "" {
smgEv[utils.SUPPLIER] = smaEv.Supplier()
}
for extraKey, extraVal := range smaEv.ExtraParameters() { // Append extraParameters
smgEv[extraKey] = extraVal
}
return &smgEv
}
// Updates fields in smgEv based on own fields
// Using pointer so we update it directly in cache
func (smaEv *SMAsteriskEvent) UpdateSMGEvent(smgEv *sessions.SMGenericEvent) error {
resSMGEv := *smgEv
switch smaEv.EventType() {
case ARIChannelStateChange:
if smaEv.ChannelState() == channelUp {
resSMGEv[utils.EVENT_NAME] = SMASessionStart
resSMGEv[utils.AnswerTime] = smaEv.Timestamp()
}
case ARIChannelDestroyed:
resSMGEv[utils.EVENT_NAME] = SMASessionTerminate
resSMGEv[utils.DISCONNECT_CAUSE] = smaEv.DisconnectCause()
if _, hasIt := resSMGEv[utils.AnswerTime]; !hasIt {
resSMGEv[utils.Usage] = "0s"
} else {
if aTime, err := smgEv.GetAnswerTime(utils.META_DEFAULT, ""); err != nil {
return err
} else if aTime.IsZero() {
resSMGEv[utils.Usage] = "0s"
} else {
actualTime, err := utils.ParseTimeDetectLayout(smaEv.Timestamp(), "")
if err != nil {
return err
}
resSMGEv[utils.Usage] = actualTime.Sub(aTime).String()
}
}
}
*smgEv = resSMGEv
return nil
}
func (smaEv *SMAsteriskEvent) UpdateCGREvent(cgrEv *utils.CGREvent) error {
resCGREv := *cgrEv
switch smaEv.EventType() {
case ARIChannelStateChange:
if smaEv.ChannelState() == channelUp {
resCGREv.Event[utils.EVENT_NAME] = SMASessionStart
resCGREv.Event[utils.AnswerTime] = smaEv.Timestamp()
}
resCGREv.Event[utils.EVENT_NAME] = SMASessionStart
resCGREv.Event[utils.AnswerTime] = smaEv.Timestamp()
case ARIChannelDestroyed:
resCGREv.Event[utils.EVENT_NAME] = SMASessionTerminate
resCGREv.Event[utils.DISCONNECT_CAUSE] = smaEv.DisconnectCause()
@@ -305,7 +232,7 @@ func (smaEv *SMAsteriskEvent) AsMapStringInterface() (mp map[string]interface{})
mp[utils.OriginHost] = smaEv.OriginatorIP()
mp[utils.Account] = smaEv.Account()
mp[utils.Destination] = smaEv.Destination()
mp[utils.SetupTime] = smaEv.Timestamp()
mp[utils.SetupTime] = smaEv.SetupTime()
if smaEv.Supplier() != "" {
mp[utils.SUPPLIER] = smaEv.Supplier()
}
@@ -375,14 +302,10 @@ func (smaEv *SMAsteriskEvent) V1AuthorizeArgs() (args *sessions.V1AuthorizeArgs)
return
}
func (smaEv *SMAsteriskEvent) V1InitSessionArgs() (args *sessions.V1InitSessionArgs) {
cgrEv, err := smaEv.AsCGREvent(config.CgrConfig().DefaultTimezone)
if err != nil {
return
}
func (smaEv *SMAsteriskEvent) V1InitSessionArgs(cgrEv utils.CGREvent) (args *sessions.V1InitSessionArgs) {
args = &sessions.V1InitSessionArgs{ // defaults
InitSession: true,
CGREvent: *cgrEv,
CGREvent: cgrEv,
}
/*
subsystems, has := kev[KamCGRSubsystems]
@@ -408,14 +331,10 @@ func (smaEv *SMAsteriskEvent) V1InitSessionArgs() (args *sessions.V1InitSessionA
return
}
func (smaEv *SMAsteriskEvent) V1TerminateSessionArgs() (args *sessions.V1TerminateSessionArgs) {
cgrEv, err := smaEv.AsCGREvent(config.CgrConfig().DefaultTimezone)
if err != nil {
return
}
func (smaEv *SMAsteriskEvent) V1TerminateSessionArgs(cgrEv utils.CGREvent) (args *sessions.V1TerminateSessionArgs) {
args = &sessions.V1TerminateSessionArgs{ // defaults
TerminateSession: true,
CGREvent: *cgrEv,
CGREvent: cgrEv,
}
/*
subsystems, has := kev[KamCGRSubsystems]

View File

@@ -21,9 +21,6 @@ import (
"encoding/json"
"reflect"
"testing"
"github.com/cgrates/cgrates/sessions"
"github.com/cgrates/cgrates/utils"
)
var (
@@ -328,212 +325,3 @@ func TestSMAEventExtraParameters(t *testing.T) {
t.Errorf("Expecting: %+v, received: %+v", expExtraParams, extraParams)
}
}
/*
func TestSMAEventUpdateFromEvent(t *testing.T) {
var ev map[string]interface{}
if err := json.Unmarshal([]byte(stasisStart), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
ev = make(map[string]interface{})
if err := json.Unmarshal([]byte(channelAnsweredDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv2 := NewSMAsteriskEvent(ev, "127.0.0.1")
smaEv.UpdateFromEvent(smaEv2)
eSMAEv := &SMAsteriskEvent{
ariEv: map[string]interface{}{
"type": "ChannelDestroyed",
"args": []interface{}{"cgr_reqtype=*prepaid", "cgr_destination=1003", "extra1=val1", "extra2=val2"},
"timestamp": "2016-09-12T13:54:27.335+0200",
"application": "cgrates_auth",
"cause_txt": "Normal Clearing",
"channel": map[string]interface{}{
"id": "1473681228.6",
"state": "Up",
"name": "PJSIP/1001-00000004",
"caller": map[string]interface{}{
"name": "1001",
"number": "1001"},
"language": "en",
"connected": map[string]interface{}{
"name": "",
"number": "1002"},
"accountcode": "",
"dialplan": map[string]interface{}{
"context": "internal",
"exten": "1002",
"priority": 3.0},
"creationtime": "2016-09-12T13:53:48.918+0200"},
"cause": 16.0},
asteriskIP: "127.0.0.1",
cachedFields: map[string]string{"cgr_reqtype": "*prepaid",
"cgr_destination": "1003", "extra1": "val1", "extra2": "val2"},
}
if !reflect.DeepEqual(eSMAEv, smaEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMAEv, smaEv)
}
}
*/
//Here
func TestSMAEventAsSMGenericEvent(t *testing.T) {
var ev map[string]interface{}
if err := json.Unmarshal([]byte(stasisStart), &ev); err != nil {
t.Error(err)
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1002",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.SUPPLIER: "supplier1",
"extra1": "val1",
"extra2": "val2",
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
if smgEv := smaEv.AsSMGenericEvent(); !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}
func TestSMAEventUpdateSMGEventAnswered(t *testing.T) {
var ev map[string]interface{}
if err := json.Unmarshal([]byte(channelStateChange), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
smgEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
"extra1": "val1",
"extra2": "val2",
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionStart,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.AnswerTime: "2016-09-12T13:53:52.110+0200",
"extra1": "val1",
"extra2": "val2",
}
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
// Apply update using a terminate event
ev = make(map[string]interface{})
if err = json.Unmarshal([]byte(channelAnsweredDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv = NewSMAsteriskEvent(ev, "127.0.0.1")
eSMGEv = &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionTerminate,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.AnswerTime: "2016-09-12T13:53:52.110+0200",
utils.Usage: "35.225s",
utils.DISCONNECT_CAUSE: "Normal Clearing",
"extra1": "val1",
"extra2": "val2",
}
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}
func TestSMAEventUpdateSMGEventUnaswered(t *testing.T) {
smgEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
"extra1": "val1",
"extra2": "val2",
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionTerminate,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.Usage: "0s",
utils.DISCONNECT_CAUSE: "Normal Clearing",
"extra1": "val1",
"extra2": "val2",
}
// Apply update using a terminate event
ev := make(map[string]interface{})
if err := json.Unmarshal([]byte(channelUnansweredDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}
func TestSMAEventUpdateSMGEventBusy(t *testing.T) {
smgEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMAAuthorization,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
"extra1": "val1",
"extra2": "val2",
}
eSMGEv := &sessions.SMGenericEvent{
utils.EVENT_NAME: SMASessionTerminate,
utils.OriginID: "1473681228.6",
utils.RequestType: "*prepaid",
utils.OriginHost: "127.0.0.1",
utils.Account: "1001",
utils.Destination: "1003",
utils.SetupTime: "2016-09-12T13:53:48.919+0200",
utils.Usage: "0s",
utils.DISCONNECT_CAUSE: "User busy",
"extra1": "val1",
"extra2": "val2",
}
// Apply update using a terminate event
ev := make(map[string]interface{})
if err := json.Unmarshal([]byte(channelBusyDestroyed), &ev); err != nil {
t.Error(err)
}
smaEv := NewSMAsteriskEvent(ev, "127.0.0.1")
if err := smaEv.UpdateSMGEvent(smgEv); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eSMGEv, smgEv) {
t.Errorf("Expecting: %+v, received: %+v", eSMGEv, smgEv)
}
}

View File

@@ -136,8 +136,6 @@ func (sma *AsteriskAgent) handleStasisStart(ev *SMAsteriskEvent) {
utils.AsteriskAgent, ev.ChannelID()))
return
}
// var maxUsage float64
// smgEv := ev.AsSMGenericEvent()
var authReply sessions.V1AuthorizeReply
if err := sma.smg.Call(utils.SessionSv1AuthorizeEvent, authArgs, &authReply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to authorize session for channelID: %s",
@@ -161,7 +159,7 @@ func (sma *AsteriskAgent) handleStasisStart(ev *SMAsteriskEvent) {
sma.cgrCfg.AsteriskAgentCfg().AsteriskConns[sma.astConnIdx].Address,
ev.ChannelID(), CGRMaxSessionTime),
url.Values{"value": {strconv.FormatFloat(
float64(authReply.MaxUsage.Nanoseconds())*1000, 'f', -1, 64)}}); err != nil { // Asterisk expects value in ms
float64(authReply.MaxUsage.Miliseconds(), 'f', -1, 64)}}); err != nil { // Asterisk expects value in ms
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when setting %s for channelID: %s",
utils.AsteriskAgent, err.Error(), CGRMaxSessionTime, ev.ChannelID()))
// Since we got error, disconnect channel
@@ -190,18 +188,33 @@ func (sma *AsteriskAgent) handleChannelStateChange(ev *SMAsteriskEvent) {
return
}
sma.evCacheMux.RLock()
_, hasIt := sma.eventsCache[ev.ChannelID()]
cgrEv, hasIt := sma.eventsCache[ev.ChannelID()]
sma.evCacheMux.RUnlock()
if !hasIt { // Not handled by us
return
}
initSessionArgs := ev.V1InitSessionArgs()
sma.evCacheMux.Lock()
err := ev.UpdateCGREvent(cgrEv) // Updates the event directly in the cache
sma.evCacheMux.Unlock()
if err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> Error: %s when attempting to initiate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
// populate init session args
initSessionArgs := ev.V1InitSessionArgs(*cgrEv)
if initSessionArgs == nil {
utils.Logger.Err(fmt.Sprintf("<%s> event: %s cannot generate init session arguments",
utils.AsteriskAgent, ev.ChannelID()))
return
}
//initit Session
var initReply sessions.V1InitSessionReply
if err := sma.smg.Call(utils.SessionSv1InitiateSession,
@@ -223,63 +236,19 @@ func (sma *AsteriskAgent) handleChannelStateChange(ev *SMAsteriskEvent) {
}
return
}
sma.evCacheMux.Lock()
err := ev.UpdateCGREvent(&initSessionArgs.CGREvent) // Updates the event directly in the cache
sma.evCacheMux.Unlock()
if err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> Error: %s when attempting to initiate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
if err := sma.hangupChannel(ev.ChannelID()); err != nil {
utils.Logger.Err(
fmt.Sprintf("<%s> Error: %s when attempting to disconnect channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
return
}
}
// Channel disconnect
func (sma *AsteriskAgent) handleChannelDestroyed(ev *SMAsteriskEvent) {
sma.evCacheMux.RLock()
_, hasIt := sma.eventsCache[ev.ChannelID()]
cgrEv, hasIt := sma.eventsCache[ev.ChannelID()]
sma.evCacheMux.RUnlock()
if !hasIt { // Not handled by us
return
}
//terminate session
tsArgs := ev.V1TerminateSessionArgs()
if tsArgs == nil {
utils.Logger.Err(fmt.Sprintf("<%s> event: %s cannot generate terminate session arguments",
utils.AsteriskAgent, ev.ChannelID()))
return
}
var reply string
if err := sma.smg.Call(utils.SessionSv1TerminateSession,
tsArgs, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to terminate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
if sma.cgrCfg.AsteriskAgentCfg().CreateCDR {
setupTime, err := utils.ParseTimeDetectLayout(
ev.SetupTime(), config.CgrConfig().DefaultTimezone)
if err != nil {
return
}
cgrEv := utils.CGREvent{
Tenant: ev.Tenant(),
ID: utils.UUIDSha1Prefix(),
Time: &setupTime,
Event: ev.AsMapStringInterface(),
}
if err := sma.smg.Call(utils.SessionSv1ProcessCDR, cgrEv, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to process CDR for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
}
sma.evCacheMux.Lock()
err := ev.UpdateCGREvent(&tsArgs.CGREvent) // Updates the event directly in the cache
err := ev.UpdateCGREvent(cgrEv) // Updates the event directly in the cache
sma.evCacheMux.Unlock()
if err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to initiate session for channelID: %s",
@@ -290,6 +259,27 @@ func (sma *AsteriskAgent) handleChannelDestroyed(ev *SMAsteriskEvent) {
}
return
}
// populate terminate session args
tsArgs := ev.V1TerminateSessionArgs(*cgrEv)
if tsArgs == nil {
utils.Logger.Err(fmt.Sprintf("<%s> event: %s cannot generate terminate session arguments",
utils.AsteriskAgent, ev.ChannelID()))
return
}
var reply string
if err := sma.smg.Call(utils.SessionSv1TerminateSession,
tsArgs, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to terminate session for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
if sma.cgrCfg.AsteriskAgentCfg().CreateCDR {
if err := sma.smg.Call(utils.SessionSv1ProcessCDR, *cgrEv, &reply); err != nil {
utils.Logger.Err(fmt.Sprintf("<%s> Error: %s when attempting to process CDR for channelID: %s",
utils.AsteriskAgent, err.Error(), ev.ChannelID()))
}
}
}
// Called to shutdown the service

View File

@@ -1,7 +1,7 @@
[simpletrans]
type=transport
protocol=udp
bind=192.168.56.203
bind=0.0.0.0
[1001]
type = endpoint

View File

@@ -4,28 +4,24 @@
// Copyright (C) ITsysCOM GmbH
"general": {
"http_skip_tls_verify": false, // if enabled Http Client will accept any TLS certificate
"rounding_decimals": 5, // system level precision for floats
"dbdata_encoding": "msgpack", // encoding used to store object data in strings: <msgpack|json>
"tpexport_dir": "/var/spool/cgrates/tpe", // path towards export folder for offline Tariff Plans
"httpposter_attempts": 3, // number of http attempts before considering request failed (eg: *call_url)
"default_request_type": "*rated", // default request type to consider when missing from requests: <""|*prepaid|*postpaid|*pseudoprepaid|*rated>
"default_category": "call", // default category to consider when missing from requests
"default_tenant": "cgrates.org", // default tenant to consider when missing from requests
"default_timezone": "Local", // default timezone for timestamps where not specified <""|UTC|Local|$IANA_TZ_DB>
"connect_attempts": 3, // initial server connect attempts
"reconnects": -1, // number of retries in case of connection lost
"connect_timeout": "1s", // consider connection unsuccessful on timeout, 0 to disable the feature
"reply_timeout": "2s", // consider connection down for replies taking longer than this value
"response_cache_ttl": "0s", // the life span of a cached response
"internal_ttl": "2m", // maximum duration to wait for internal connections before giving up
"locking_timeout": "5s", // timeout internal locks to avoid deadlocks
"cache_dump_dir": "", // cache dump for faster start (leave empty to disable)
"log_level": 7,
},
"stor_db": { // database used to store offline tariff plans and CDRs
"db_password": "CGRateS.org", // password to use when connecting to stordb
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",
"http": ":2080",
},
"stor_db": {
"db_password": "CGRateS.org",
},
"scheduler": {
"enabled": true,
},
@@ -84,9 +80,6 @@
"debit_interval": "10s",
},
"scheduler": {
"enabled": true,
},
"cdre": {
"*default": {

View File

@@ -1,15 +1,13 @@
{
// Real-time Charging System for Telecom & ISP environments
// Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
// Copyright (C) ITsysCOM GmbH
//
// This file contains the default configuration hardcoded into CGRateS.
// This is what you get when you load CGRateS with an empty configuration file.
"general": {
"log_level": 7,
},
"listen": {
"rpc_json": ":2012",
"rpc_gob": ":2013",

View File

@@ -1,484 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
const astPassword = "CGRateS.org"
var tutAstCallsCfg *config.CGRConfig
var tutAstCallsRpc *rpc.Client
var tutAstCallsPjSuaListener *os.File
func TestTutAstCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutAstCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "asterisk_ari", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutAstCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutAstCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutAstCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutAstCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutAstCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutAstCallsCfg); err != nil {
t.Fatal(err)
}
}
// start Asterisk server
func TestTutAstCallsStartAsterisk(t *testing.T) {
engine.KillProcName("asterisk", 1000)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "asterisk_ari", "asterisk", "etc", "init.d", "asterisk"), "start", 2000); err != nil {
t.Fatal(err)
}
}
// Start CGR Engine
func TestTutAstCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "asterisk_ari", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
/*
// Restart FS so we make sure reconnects are working
func TestTutAstCallsRestartAsterisk(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "asterisk_ari", "asterisk", "etc", "init.d", "asterisk"), "restart", 2000); err != nil {
t.Fatal(err)
}
}
*/
// Connect rpc client to rater
func TestTutAstCallsRpcConn(t *testing.T) {
var err error
tutAstCallsRpc, err = jsonrpc.Dial("tcp", tutAstCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutAstCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutAstCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != utils.OK {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutAstCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Error("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutAstCallsCdrStatsBefore(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.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
if err := tutAstCallsRpc.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 := tutAstCallsRpc.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 := tutAstCallsRpc.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 := tutAstCallsRpc.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 := tutAstCallsRpc.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 := tutAstCallsRpc.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 TestTutAstCallsStartPjsuaListener(t *testing.T) {
var err error
acnts := []*engine.PjsuaAccount{
&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"},
&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: astPassword, Realm: "*", Registrar: "sip:127.0.0.1:5060"}}
if tutAstCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, time.Duration(500*time.Millisecond)); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutAstCallsCall1001To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: astPassword, 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 TestTutAstCallsCall1001To1003(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1001@127.0.0.1", Username: "1001", Password: astPassword, 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 TestTutAstCallsCall1002To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1002@127.0.0.1", Username: "1002", Password: astPassword, Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(61)*time.Second, 5073); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1003To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1", Username: "1003", Password: astPassword, Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(63)*time.Second, 5074); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1004To1001(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1004@127.0.0.1", Username: "1004", Password: astPassword, Realm: "*"}, "sip:1001@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(62)*time.Second, 5075); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1006To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1006@127.0.0.1", Username: "1006", Password: astPassword, Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(64)*time.Second, 5076); err != nil {
t.Fatal(err)
}
}
func TestTutAstCallsCall1007To1002(t *testing.T) {
if err := engine.PjsuaCallUri(&engine.PjsuaAccount{Id: "sip:1007@127.0.0.1", Username: "1007", Password: astPassword, Realm: "*"}, "sip:1002@127.0.0.1",
"sip:127.0.0.1:5060", time.Duration(66)*time.Second, 5077); err != nil {
t.Fatal(err)
}
}
// Make sure account was debited properly
func TestTutAstCallsAccount1001(t *testing.T) {
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"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutAstCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var cgrId string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
if err := tutAstCallsRpc.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].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
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])
//}
}
req = utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1003"}}
if err := tutAstCallsRpc.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].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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])
}
}
req = utils.RPCCDRsFilter{Accounts: []string{"1001"}, RunIDs: []string{"derived_run1"}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutAstCalls1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1002"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutAstCalls1003Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1003"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.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].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
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])
}
}
}
// Make sure account was debited properly
func TestTutAstCalls1004Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1004"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.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].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure aliasing was done for 1006 and we have no CDRs for it
func TestTutAstCalls1006Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1006"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutAstCalls1007Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{Accounts: []string{"1007"}, RunIDs: []string{utils.META_DEFAULT}}
if err := tutAstCallsRpc.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].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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])
}
}
}
// Make sure account was debited properly
func TestTutAstCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutAstCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutAstCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutAstCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutAstCallsStopPjsuaListener(t *testing.T) {
time.Sleep(70 * time.Second) // Give time for calls to go through before unregistering
tutAstCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutAstCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutAstCallsStopAsterisk(t *testing.T) {
engine.KillProcName("asterisk", 100)
}

View File

@@ -1,4 +1,4 @@
// +build newcall
// +build call
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
@@ -22,7 +22,6 @@ package general_tests
import (
"flag"
"fmt"
"net/rpc"
"net/rpc/jsonrpc"
"os"
@@ -37,14 +36,15 @@ import (
"github.com/cgrates/cgrates/utils"
)
var tutFsCallsCfg *config.CGRConfig
var tutFsCallsRpc *rpc.Client
var tutFsCallsPjSuaListener *os.File
var tutorialCallsCfg *config.CGRConfig
var tutorialCallsRpc *rpc.Client
var tutorialCallsPjSuaListener *os.File
var waitRater = flag.Int("wait_rater", 100, "Number of miliseconds to wait for rater to start and cache")
var dataDir = flag.String("data_dir", "/usr/share/cgrates", "CGR data dir path here")
var fsConfig = flag.String("fsConfig", "/usr/share/cgrates/tutorials/fs_evsock", "FreeSwitch tutorial folder")
var kamConfig = flag.String("kamConfig", "/usr/share/cgrates/tutorials/kamevapi", "Kamailio tutorial folder")
var oSipsConfig = flag.String("osConfig", "/usr/share/cgrates/tutorials/osips_native", "OpenSips tutorial folder")
var ariConf = flag.String("ariConf", "/usr/share/cgrates/tutorials/asterisk_ari", "Asterisk tutorial folder")
var optConf string
var sTestsCalls = []func(t *testing.T){
@@ -80,61 +80,73 @@ var sTestsCalls = []func(t *testing.T){
}
//Test start here
func TestFSCalls(t *testing.T) {
func TestFreeswitchCalls(t *testing.T) {
optConf = utils.Freeswitch
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
func TestKamCalls(t *testing.T) {
func TestKamailioCalls(t *testing.T) {
optConf = utils.Kamailio
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
func TestOSCalls(t *testing.T) {
func TestOpensipsCalls(t *testing.T) {
optConf = utils.Opensips
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
func TestAsteriskCalls(t *testing.T) {
optConf = utils.Asterisk
for _, stest := range sTestsCalls {
t.Run("", stest)
}
}
func testCallInitCfg(t *testing.T) {
// Init config first
var err error
if optConf == utils.Freeswitch {
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*fsConfig, "cgrates", "etc", "cgrates"))
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*fsConfig, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
} else if optConf == utils.Kamailio {
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*kamConfig, "cgrates", "etc", "cgrates"))
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*kamConfig, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
} else if optConf == utils.Opensips {
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*oSipsConfig, "cgrates", "etc", "cgrates"))
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*oSipsConfig, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
} else if optConf == utils.Asterisk {
tutorialCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*ariConf, "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
}
tutFsCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutFsCallsCfg)
tutorialCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutorialCallsCfg)
}
// Remove data in both rating and accounting db
func testCallResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutFsCallsCfg); err != nil {
if err := engine.InitDataDb(tutorialCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func testCallResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutFsCallsCfg); err != nil {
if err := engine.InitStorDb(tutorialCallsCfg); err != nil {
t.Fatal(err)
}
}
@@ -156,6 +168,11 @@ func testCallStartFS(t *testing.T) {
if err := engine.CallScript(path.Join(*oSipsConfig, "opensips", "etc", "init.d", "opensips"), "start", 3000); err != nil {
t.Fatal(err)
}
} else if optConf == utils.Asterisk {
engine.KillProcName(utils.Asterisk, 5000)
if err := engine.CallScript(path.Join(*ariConf, "asterisk", "etc", "init.d", "asterisk"), "start", 3000); err != nil {
t.Fatal(err)
}
}
}
@@ -174,6 +191,10 @@ func testCallStartEngine(t *testing.T) {
if err := engine.CallScript(path.Join(*oSipsConfig, "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
} else if optConf == utils.Asterisk {
if err := engine.CallScript(path.Join(*ariConf, "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
}
@@ -191,13 +212,17 @@ func testCallRestartFS(t *testing.T) {
if err := engine.CallScript(path.Join(*oSipsConfig, "opensips", "etc", "init.d", "opensips"), "restart", 5000); err != nil {
t.Fatal(err)
}
} else if optConf == utils.Asterisk {
if err := engine.CallScript(path.Join(*ariConf, "asterisk", "etc", "init.d", "asterisk"), "restart", 5000); err != nil {
t.Fatal(err)
}
}
}
// Connect rpc client to rater
func testCallRpcConn(t *testing.T) {
var err error
tutFsCallsRpc, err = jsonrpc.Dial("tcp", tutFsCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
tutorialCallsRpc, err = jsonrpc.Dial("tcp", tutorialCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
@@ -208,7 +233,7 @@ func testCallRpcConn(t *testing.T) {
func testCallLoadTariffPlanFromFolder(t *testing.T) {
var reply string
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial2")}
if err := tutFsCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
@@ -218,14 +243,14 @@ func testCallLoadTariffPlanFromFolder(t *testing.T) {
func testCallAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
var reply2 *engine.Account
attrs2 := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs2, &reply2); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetAccount", attrs2, &reply2); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply2.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 {
t.Errorf("Calling ApierV1.GetBalance received: %f", reply2.BalanceMap[utils.MONETARY].GetTotalValue())
@@ -238,7 +263,7 @@ func testCallStatMetricsBefore(t *testing.T) {
utils.MetaTCC: utils.NOT_AVAILABLE,
utils.MetaTCD: utils.NOT_AVAILABLE,
}
if err := tutFsCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
if err := tutorialCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
&utils.TenantID{Tenant: "cgrates.org", ID: "Stats2"}, &metrics); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expectedMetrics, metrics) {
@@ -257,7 +282,7 @@ func testCallCheckResourceBeforeAllocation(t *testing.T) {
utils.Subject: "1001",
utils.Destination: "1002"},
}}
if err := tutFsCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
if err := tutorialCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
t.Error(err)
} else if len(*rs) != 2 {
t.Errorf("Resources: %+v", utils.ToJSON(rs))
@@ -273,7 +298,7 @@ func testCallCheckResourceBeforeAllocation(t *testing.T) {
func testCallCheckThreshold1001Before(t *testing.T) {
var td engine.Threshold
eTd := engine.Threshold{Tenant: "cgrates.org", ID: "THD_ACNT_1001", Hits: 0}
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1001"}, &td); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eTd, td) {
@@ -284,7 +309,7 @@ func testCallCheckThreshold1001Before(t *testing.T) {
func testCallCheckThreshold1002Before(t *testing.T) {
var td engine.Threshold
eTd := engine.Threshold{Tenant: "cgrates.org", ID: "THD_ACNT_1002", Hits: 0}
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1002"}, &td); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eTd, td) {
@@ -303,7 +328,7 @@ func testCallStartPjsuaListener(t *testing.T) {
&engine.PjsuaAccount{Id: "sip:1003@127.0.0.1",
Username: "1003", Password: "CGRateS.org", Realm: "*", Registrar: "sip:127.0.0.1:5080"},
}
if tutFsCallsPjSuaListener, err = engine.StartPjsuaListener(
if tutorialCallsPjSuaListener, err = engine.StartPjsuaListener(
acnts, 5070, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
@@ -332,7 +357,7 @@ func testCallGetActiveSessions(t *testing.T) {
Destination: "1002",
},
}
if err := tutFsCallsRpc.Call(utils.SessionSv1GetActiveSessions,
if err := tutorialCallsRpc.Call(utils.SessionSv1GetActiveSessions,
&map[string]string{}, &reply); err != nil {
t.Error("Got error on SessionSv1.GetActiveSessions: ", err.Error())
} else {
@@ -377,7 +402,7 @@ func testCallCheckResourceAllocation(t *testing.T) {
utils.Subject: "1001",
utils.Destination: "1002"},
}}
if err := tutFsCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
if err := tutorialCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
t.Error(err)
} else if len(*rs) != 2 {
t.Errorf("Resources: %+v", utils.ToJSON(rs))
@@ -395,7 +420,7 @@ func testCallAccount1001(t *testing.T) {
time.Sleep(time.Duration(80) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error(err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Expected: 10, received: %+v", reply.BalanceMap[utils.MONETARY].GetTotalValue())
@@ -408,15 +433,12 @@ func testCallAccount1001(t *testing.T) {
func testCall1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
if err := tutorialCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
for _, cdr := range reply {
if cdr.Source != "freeswitch_json" && cdr.Source != "SMG_KamailioAgent" {
t.Errorf("Unexpected Source for CDR: %+v", cdr.Source)
}
if cdr.RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", cdr.RequestType)
}
@@ -443,14 +465,11 @@ func testCall1001Cdrs(t *testing.T) {
func testCall1002Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1002"}, DestinationPrefixes: []string{"1001"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
if err := tutorialCallsRpc.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].Source != "freeswitch_json" && reply[0].Source != "SMG_KamailioAgent" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0].Source)
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0].RequestType)
}
@@ -473,7 +492,7 @@ func testCallStatMetrics(t *testing.T) {
utils.MetaTCC: "1.34009",
utils.MetaTCD: "2m24s",
}
if err := tutFsCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
if err := tutorialCallsRpc.Call(utils.StatSv1GetQueueStringMetrics,
&utils.TenantID{Tenant: "cgrates.org", ID: "Stats2"}, &metrics); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(expectedMetrics1, metrics) &&
@@ -493,7 +512,7 @@ func testCallCheckResourceRelease(t *testing.T) {
utils.Subject: "1001",
utils.Destination: "1002"},
}}
if err := tutFsCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
if err := tutorialCallsRpc.Call(utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil {
t.Error(err)
} else if len(*rs) != 2 {
t.Errorf("Resources: %+v", rs)
@@ -508,7 +527,7 @@ func testCallCheckResourceRelease(t *testing.T) {
func testCallCheckThreshold1001After(t *testing.T) {
var td engine.Threshold
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1001"}, &td); err != nil &&
err.Error() != utils.ErrNotFound.Error() {
t.Error(err)
@@ -518,7 +537,7 @@ func testCallCheckThreshold1001After(t *testing.T) {
func testCallCheckThreshold1002After(t *testing.T) {
var td engine.Threshold
eTd := engine.Threshold{Tenant: "cgrates.org", ID: "THD_ACNT_1002", Hits: 4}
if err := tutFsCallsRpc.Call(utils.ThresholdSv1GetThreshold,
if err := tutorialCallsRpc.Call(utils.ThresholdSv1GetThreshold,
&utils.TenantID{Tenant: "cgrates.org", ID: "THD_ACNT_1002"}, &td); err != nil {
t.Error(err)
} else if !reflect.DeepEqual(eTd.Tenant, td.Tenant) {
@@ -531,8 +550,8 @@ func testCallCheckThreshold1002After(t *testing.T) {
}
func testCallStopPjsuaListener(t *testing.T) {
tutFsCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
tutorialCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func testCallStopCgrEngine(t *testing.T) {
@@ -548,5 +567,7 @@ func testCallStopFS(t *testing.T) {
engine.KillProcName(utils.Kamailio, 1000)
} else if optConf == utils.Opensips {
engine.KillProcName(utils.Opensips, 1000)
} else if optConf == utils.Asterisk {
engine.KillProcName(utils.Asterisk, 1000)
}
}

View File

@@ -1,497 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var tutFsCallsCfg *config.CGRConfig
var tutFsCallsRpc *rpc.Client
var tutFsCallsPjSuaListener *os.File
func TestTutFsCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutFsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "fs_evsock", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutFsCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutFsCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutFsCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutFsCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutFsCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutFsCallsCfg); err != nil {
t.Fatal(err)
}
}
// start FS server
func TestTutFsCallsStartFS(t *testing.T) {
engine.KillProcName("freeswitch", 5000)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "freeswitch", "etc", "init.d", "freeswitch"), "start", 3000); err != nil {
t.Fatal(err)
}
}
// Start CGR Engine
func TestTutFsCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
// Restart FS so we make sure reconnects are working
func TestTutFsCallsRestartFS(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "fs_evsock", "freeswitch", "etc", "init.d", "freeswitch"), "restart", 5000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestTutFsCallsRpcConn(t *testing.T) {
var err error
tutFsCallsRpc, err = jsonrpc.Dial("tcp", tutFsCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutFsCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutFsCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != "OK" {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutFsCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Errorf("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutFsCallsCdrStatsBefore(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.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) {
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: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, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutFsCallsCall1001To1002(t *testing.T) {
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 TestTutFsCallsCall1001To1003(t *testing.T) {
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 TestTutFsCallsCall1002To1001(t *testing.T) {
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)
}
}
func TestTutFsCallsCall1003To1001(t *testing.T) {
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)
}
}
func TestTutFsCallsCall1004To1001(t *testing.T) {
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)
}
}
func TestTutFsCallsCall1006To1002(t *testing.T) {
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)
}
}
func TestTutFsCallsCall1007To1002(t *testing.T) {
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)
}
}
// Make sure account was debited properly
func TestTutFsCallsAccount1001(t *testing.T) {
time.Sleep(time.Duration(80) * time.Second) // Allow calls to finish before start querying the results
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutFsCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var CGRID string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
if err := tutFsCallsRpc.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].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Usage != "67" && reply[0].Usage != "68" { // 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 := tutFsCallsRpc.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"}, DestinationPrefixes: []string{"1003"}}
if err := tutFsCallsRpc.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].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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 := tutFsCallsRpc.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"}}
if err := tutFsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutFsCalls1002Cdrs(t *testing.T) {
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) != 2 { // Should be counted here also call originated form 1006 which is aliased to 1002
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutFsCalls1003Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
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])
}
}
}
// Make sure account was debited properly
func TestTutFsCalls1004Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure we don't have any CDRs for 1006 since it should have been aliased to 1002
func TestTutFsCalls1006Cdrs(t *testing.T) {
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) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutFsCalls1007Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "freeswitch_json" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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])
}
}
}
// Make sure account was debited properly
func TestTutFsCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutFsCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutFsCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutFsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutFsCallsStopPjsuaListener(t *testing.T) {
tutFsCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutFsCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutFsCallsStopFS(t *testing.T) {
engine.KillProcName("freeswitch", 1000)
}

View File

@@ -1,496 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var tutKamCallsCfg *config.CGRConfig
var tutKamCallsRpc *rpc.Client
var tutKamCallsPjSuaListener *os.File
func TestTutKamCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutKamCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "kamevapi", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutKamCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutKamCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutKamCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutKamCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutKamCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutKamCallsCfg); err != nil {
t.Fatal(err)
}
}
// start FS server
func TestTutKamCallsStartKamailio(t *testing.T) {
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)
}
}
// Start CGR Engine
func TestTutKamCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
// Restart FS so we make sure reconnects are working
func TestTutKamCallsRestartKamailio(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "kamevapi", "kamailio", "etc", "init.d", "kamailio"), "restart", 3000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestTutKamCallsRpcConn(t *testing.T) {
var err error
tutKamCallsRpc, err = jsonrpc.Dial("tcp", tutKamCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutKamCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutKamCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != "OK" {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutKamCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Errorf("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
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.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) {
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 := 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: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
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.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -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.ACD: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
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: -1, engine.ASR: -1, engine.TCC: -1, engine.TCD: -1, engine.ACC: -1}
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)
}
}
// Start Pjsua as listener and register it to receive calls
func TestTutKamCallsStartPjsuaListener(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"}}
if tutKamCallsPjSuaListener, err = engine.StartPjsuaListener(acnts, 5070, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutKamCallsCall1001To1002(t *testing.T) {
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",
"sip:127.0.0.1:5060", time.Duration(67)*time.Second, 5071); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1003
func TestTutKamCallsCall1001To1003(t *testing.T) {
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 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, 5073); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1003To1001(t *testing.T) {
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, 5074); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1004To1001(t *testing.T) {
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, 5075); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1006To1002(t *testing.T) {
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, 5076); err != nil {
t.Fatal(err)
}
}
func TestTutKamCallsCall1007To1002(t *testing.T) {
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, 5077); err != nil {
t.Fatal(err)
}
}
// Make sure account was debited properly
func TestTutKamCallsAccount1001(t *testing.T) {
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"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutKamCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var cgrId string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []string{"1002"}}
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].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
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 := 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"}, DestinationPrefixes: []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].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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"}}
if err := tutKamCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1002Cdrs(t *testing.T) {
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) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1003Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
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])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1004Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutKamCalls1006Cdrs(t *testing.T) {
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) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutKamCalls1007Cdrs(t *testing.T) {
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].Source != "KAMAILIO_CGR_CALL_END" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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])
}
}
}
// Make sure account was debited properly
func TestTutKamCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutKamCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutKamCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutKamCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutKamCallsStopPjsuaListener(t *testing.T) {
tutKamCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutKamCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutKamCallsStopKam(t *testing.T) {
engine.KillProcName("kamailio", 1000)
}

View File

@@ -1,496 +0,0 @@
// +build calls
/*
Real-time Online/Offline Charging System (OCS) for Telecom & ISP environments
Copyright (C) ITsysCOM GmbH
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package general_tests
import (
"net/rpc"
"net/rpc/jsonrpc"
"os"
"path"
"reflect"
"testing"
"time"
"github.com/cgrates/cgrates/apier/v1"
"github.com/cgrates/cgrates/config"
"github.com/cgrates/cgrates/engine"
"github.com/cgrates/cgrates/utils"
)
var tutOsipsCallsCfg *config.CGRConfig
var tutOsipsCallsRpc *rpc.Client
var tutOsipsCallsPjSuaListener *os.File
func TestTutOsipsCallsInitCfg(t *testing.T) {
// Init config first
var err error
tutOsipsCallsCfg, err = config.NewCGRConfigFromFolder(path.Join(*dataDir, "tutorials", "osips_async", "cgrates", "etc", "cgrates"))
if err != nil {
t.Error(err)
}
tutOsipsCallsCfg.DataFolderPath = *dataDir // Share DataFolderPath through config towards StoreDb for Flush()
config.SetCgrConfig(tutOsipsCallsCfg)
}
// Remove data in both rating and accounting db
func TestTutOsipsCallsResetDataDb(t *testing.T) {
if err := engine.InitDataDb(tutOsipsCallsCfg); err != nil {
t.Fatal(err)
}
}
// Wipe out the cdr database
func TestTutOsipsCallsResetStorDb(t *testing.T) {
if err := engine.InitStorDb(tutOsipsCallsCfg); err != nil {
t.Fatal(err)
}
}
// start FS server
func TestTutOsipsCallsStartOsips(t *testing.T) {
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)
}
}
// Start CGR Engine
func TestTutOsipsCallsStartEngine(t *testing.T) {
engine.KillProcName("cgr-engine", *waitRater)
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "cgrates", "etc", "init.d", "cgrates"), "start", 100); err != nil {
t.Fatal(err)
}
}
// Restart FS so we make sure reconnects are working
func TestTutOsipsCallsRestartOsips(t *testing.T) {
if err := engine.CallScript(path.Join(*dataDir, "tutorials", "osips_async", "opensips", "etc", "init.d", "opensips"), "restart", 3000); err != nil {
t.Fatal(err)
}
}
// Connect rpc client to rater
func TestTutOsipsCallsRpcConn(t *testing.T) {
var err error
tutOsipsCallsRpc, err = jsonrpc.Dial("tcp", tutOsipsCallsCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed
if err != nil {
t.Fatal(err)
}
}
// Load the tariff plan, creating accounts and their balances
func TestTutOsipsCallsLoadTariffPlanFromFolder(t *testing.T) {
reply := ""
attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "tutorial")}
if err := tutOsipsCallsRpc.Call("ApierV1.LoadTariffPlanFromFolder", attrs, &reply); err != nil {
t.Error(err)
} else if reply != "OK" {
t.Error(reply)
}
time.Sleep(time.Duration(*waitRater) * time.Millisecond) // Give time for scheduler to execute topups
}
// Make sure account was debited properly
func TestTutOsipsCallsAccountsBefore(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1002"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1003"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1004"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1007"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() != 0.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
}
attrs = &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1005"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() {
t.Error("Got error on ApierV2.GetAccount: %v", err)
}
}
// Make sure all stats queues are in place
func TestTutOsipsCallsCdrStatsBefore(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.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) {
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: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, time.Duration(*waitRater)*time.Millisecond); err != nil {
t.Fatal(err)
}
}
// Call from 1001 (prepaid) to 1002
func TestTutOsipsCallsCall1001To1002(t *testing.T) {
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 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 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)
}
}
func TestTutOsipsCallsCall1003To1001(t *testing.T) {
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)
}
}
func TestTutOsipsCallsCall1004To1001(t *testing.T) {
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)
}
}
func TestTutOsipsCallsCall1006To1002(t *testing.T) {
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)
}
}
func TestTutOsipsCallsCall1007To1002(t *testing.T) {
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)
}
}
// Make sure account was debited properly
func TestTutOsipsCallsAccount1001(t *testing.T) {
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"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.BalanceMap[utils.MONETARY].GetTotalValue() == 10.0 { // Make sure we debitted
t.Errorf("Calling ApierV1.GetBalance received: %f", reply.BalanceMap[utils.MONETARY].GetTotalValue())
} else if reply.Disabled == true {
t.Error("Account disabled")
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1001Cdrs(t *testing.T) {
var reply []*engine.ExternalCDR
//var cgrId string // Share with getCostDetails
//var cCost engine.CallCost
req := utils.RPCCDRsFilter{RunIDs: []string{utils.META_DEFAULT}, Accounts: []string{"1001"}, DestinationPrefixes: []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].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
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"}, DestinationPrefixes: []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].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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"}}
if err := tutOsipsCallsRpc.Call("ApierV2.GetCdrs", req, &reply); err != nil {
t.Error("Unexpected error: ", err.Error())
} else if len(reply) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Subject != "1002" {
t.Errorf("Unexpected Subject for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1002Cdrs(t *testing.T) {
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) != 2 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_POSTPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "61" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1003Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PSEUDOPREPAID {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
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])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1004Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_RATED {
t.Errorf("Unexpected RequestType for CDR: %+v", reply[0])
}
if reply[0].Destination != "1001" {
t.Errorf("Unexpected Destination for CDR: %+v", reply[0])
}
if reply[0].Usage != "62" && reply[0].Usage != "63" { // Usage as seconds
t.Errorf("Unexpected Usage for CDR: %+v", reply[0])
}
}
}
// Make sure aliasing was done for 1006 and we have no CDRs for it
func TestTutOsipsCalls1006Cdrs(t *testing.T) {
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) != 0 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
}
}
// Make sure account was debited properly
func TestTutOsipsCalls1007Cdrs(t *testing.T) {
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 {
t.Error("Unexpected number of CDRs returned: ", len(reply))
} else {
if reply[0].Source != "OSIPS_E_ACC_EVENT" {
t.Errorf("Unexpected Source for CDR: %+v", reply[0])
}
if reply[0].RequestType != utils.META_PREPAID {
t.Errorf("Unexpected RequestType 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])
}
}
}
// Make sure account was debited properly
func TestTutOsipsCallsAccountFraud1001(t *testing.T) {
var reply string
attrAddBlnc := &v1.AttrAddBalance{Tenant: "cgrates.org", Account: "1001", BalanceType: "*monetary", Value: 101}
if err := tutOsipsCallsRpc.Call("ApierV1.AddBalance", attrAddBlnc, &reply); err != nil {
t.Error("Got error on ApierV1.AddBalance: ", err.Error())
} else if reply != "OK" {
t.Errorf("Calling ApierV1.AddBalance received: %s", reply)
}
}
// Based on Fraud automatic mitigation, our account should be disabled
func TestTutOsipsCallsAccountDisabled1001(t *testing.T) {
var reply *engine.Account
attrs := &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}
if err := tutOsipsCallsRpc.Call("ApierV2.GetAccount", attrs, &reply); err != nil {
t.Error("Got error on ApierV2.GetAccount: ", err.Error())
} else if reply.Disabled == false {
t.Error("Account should be disabled per fraud detection rules.")
}
}
func TestTutOsipsCallsStopPjsuaListener(t *testing.T) {
tutOsipsCallsPjSuaListener.Write([]byte("q\n")) // Close pjsua
time.Sleep(time.Duration(1) * time.Second) // Allow pjsua to finish it's tasks, eg un-REGISTER
}
func TestTutOsipsCallsStopCgrEngine(t *testing.T) {
if err := engine.KillEngine(100); err != nil {
t.Error(err)
}
}
func TestTutOsipsCallsStopOpensips(t *testing.T) {
engine.KillProcName("opensips", 100)
}

View File

@@ -535,6 +535,7 @@ const (
Freeswitch = "freeswitch"
Kamailio = "kamailio"
Opensips = "opensips"
Asterisk = "asterisk"
)
// Migrator Action