From 56ada587d2a936e406708a16cdeec1ab0ee08fbd Mon Sep 17 00:00:00 2001 From: DanB Date: Wed, 3 Oct 2018 19:43:35 +0200 Subject: [PATCH] Completing Diameter DryRun integration test --- agents/diam_it_test.go | 88 ++++++++++++++++++++----- agents/diamagent.go | 9 ++- agents/libdiam.go | 21 +++--- agents/libdiam_test.go | 74 +++++++++++++++++++++ agents/libhttpagent.go | 11 ++-- agents/librad.go | 11 ++-- data/conf/samples/diamagent/dryrun.json | 4 +- 7 files changed, 179 insertions(+), 39 deletions(-) diff --git a/agents/diam_it_test.go b/agents/diam_it_test.go index 5c27da48b..391cd7e22 100644 --- a/agents/diam_it_test.go +++ b/agents/diam_it_test.go @@ -46,7 +46,7 @@ var dmtClient *DiameterClient var rplyTimeout time.Duration -func TestDiamITInitCfg(t *testing.T) { +func TestDiamItInitCfg(t *testing.T) { daCfgPath = path.Join(*dataDir, "conf", "samples", "diamagent") // Init config first var err error @@ -60,14 +60,14 @@ func TestDiamITInitCfg(t *testing.T) { } // Remove data in both rating and accounting db -func TestDiamITResetDataDb(t *testing.T) { +func TestDiamItResetDataDb(t *testing.T) { if err := engine.InitDataDb(daCfg); err != nil { t.Fatal(err) } } // Wipe out the cdr database -func TestDiamITResetStorDb(t *testing.T) { +func TestDiamItResetStorDb(t *testing.T) { if err := engine.InitStorDb(daCfg); err != nil { t.Fatal(err) } @@ -75,14 +75,14 @@ func TestDiamITResetStorDb(t *testing.T) { /* // Start CGR Engine -func TestDiamITStartEngine(t *testing.T) { +func TestDiamItStartEngine(t *testing.T) { if _, err := engine.StopStartEngine(daCfgPath, 4000); err != nil { t.Fatal(err) } } */ -func TestDiamITConnectDiameterClient(t *testing.T) { +func TestDiamItConnectDiameterClient(t *testing.T) { dmtClient, err = NewDiameterClient(daCfg.DiameterAgentCfg().Listen, "INTEGRATION_TESTS", daCfg.DiameterAgentCfg().OriginRealm, daCfg.DiameterAgentCfg().VendorId, daCfg.DiameterAgentCfg().ProductName, @@ -93,7 +93,7 @@ func TestDiamITConnectDiameterClient(t *testing.T) { } // Connect rpc client to rater -func TestDiamITApierRpcConn(t *testing.T) { +func TestDiamItApierRpcConn(t *testing.T) { var err error apierRpc, err = jsonrpc.Dial("tcp", daCfg.RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed if err != nil { @@ -102,7 +102,7 @@ func TestDiamITApierRpcConn(t *testing.T) { } // Load the tariff plan, creating accounts and their balances -func TestDiamITTPFromFolder(t *testing.T) { +func TestDiamItTPFromFolder(t *testing.T) { attrs := &utils.AttrLoadTpFromFolder{FolderPath: path.Join(*dataDir, "tariffplans", "oldtutorial")} var loadInst utils.LoadInstance if err := apierRpc.Call("ApierV2.LoadTariffPlanFromFolder", attrs, &loadInst); err != nil { @@ -111,7 +111,7 @@ func TestDiamITTPFromFolder(t *testing.T) { time.Sleep(time.Duration(1000) * time.Millisecond) // Give time for scheduler to execute topups } -func TestDiamITDryRun(t *testing.T) { +func TestDiamItDryRun(t *testing.T) { ccr := diam.NewRequest(diam.CreditControl, 4, nil) ccr.NewAVP(avp.SessionID, avp.Mbit, 0, datatype.UTF8String("cgrates;1451911932;00082")) ccr.NewAVP(avp.OriginHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) @@ -120,7 +120,7 @@ func TestDiamITDryRun(t *testing.T) { ccr.NewAVP(avp.DestinationHost, avp.Mbit, 0, datatype.DiameterIdentity("CGR-DA")) ccr.NewAVP(avp.UserName, avp.Mbit, 0, datatype.UTF8String("CGR-DA")) ccr.NewAVP(avp.AuthApplicationID, avp.Mbit, 0, datatype.Unsigned32(4)) - ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("TestDiamITDryRun")) // Match specific DryRun profile + ccr.NewAVP(avp.ServiceContextID, avp.Mbit, 0, datatype.UTF8String("TestDiamItDryRun")) // Match specific DryRun profile ccr.NewAVP(avp.CCRequestType, avp.Mbit, 0, datatype.Enumerated(1)) ccr.NewAVP(avp.CCRequestNumber, avp.Mbit, 0, datatype.Unsigned32(1)) ccr.NewAVP(avp.EventTimestamp, avp.Mbit, 0, datatype.Time(time.Date(2016, 1, 5, 11, 30, 10, 0, time.UTC))) @@ -169,15 +169,73 @@ func TestDiamITDryRun(t *testing.T) { if msg == nil { t.Fatal("No message returned") } - avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID) - if err != nil { + // Result-Code + eVal := "2002" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Result-Code"}, dict.UndefinedVendorID); err != nil { t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { + t.Error(err) + } else if val != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) } - if len(avps) == 0 { - t.Error("Result-Code") + eVal = "cgrates;1451911932;00082" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Session-Id"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { + t.Error(err) + } else if val != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) } - eVal := "300" - if val, err := diamAVPAsString(avps[0]); err != nil { + eVal = "CGR-DA" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Origin-Host"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { + t.Error(err) + } else if val != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) + } + eVal = "cgrates.org" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Origin-Realm"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { + t.Error(err) + } else if val != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) + } + eVal = "4" + if avps, err := msg.FindAVPsWithPath([]interface{}{"Auth-Application-Id"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { + t.Error(err) + } else if val != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) + } + eVal = "1" + if avps, err := msg.FindAVPsWithPath([]interface{}{"CC-Request-Type"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { + t.Error(err) + } else if val != eVal { + t.Errorf("expecting: %s, received: <%s>", eVal, val) + } + eVal = "1" + if avps, err := msg.FindAVPsWithPath([]interface{}{"CC-Request-Number"}, dict.UndefinedVendorID); err != nil { + t.Error(err) + } else if len(avps) == 0 { + t.Error("Missing AVP") + } else if val, err := diamAVPAsString(avps[0]); err != nil { t.Error(err) } else if val != eVal { t.Errorf("expecting: %s, received: <%s>", eVal, val) diff --git a/agents/diamagent.go b/agents/diamagent.go index 3fd911b3b..39e310a91 100644 --- a/agents/diamagent.go +++ b/agents/diamagent.go @@ -147,7 +147,6 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { writeOnConn(c, m.Answer(diam.UnableToComply)) return } - fmt.Printf("After processing successfully the message, have reply: %+v\n", rply) a := m.Answer(diam.Success) // write reply into message for _, val := range rply.Values() { @@ -179,12 +178,12 @@ func (da *DiameterAgent) handleMessage(c diam.Conn, m *diam.Message) { writeOnConn(c, m.Answer(diam.UnableToComply)) return } - var apnd bool - if itm.Config == nil || !itm.Config.NewBranch { - apnd = true + var newBranch bool + if itm.Config != nil && itm.Config.NewBranch { + newBranch = true } if err := messageSetAVPsWithPath(a, itm.Path, - itmStr, apnd, da.cgrCfg.DefaultTimezone); err != nil { + itmStr, newBranch, da.cgrCfg.DefaultTimezone); err != nil { utils.Logger.Warning( fmt.Sprintf("<%s> error: %s setting reply item: %s for message: %s", utils.DiameterAgent, err.Error(), utils.ToJSON(itm), m)) diff --git a/agents/libdiam.go b/agents/libdiam.go index b8a5f193f..5f5cc403f 100644 --- a/agents/libdiam.go +++ b/agents/libdiam.go @@ -194,7 +194,7 @@ func newDiamDataType(typ datatype.TypeID, valStr, // messageAddAVPsWithPath will dynamically add AVPs into the message // append: append to the message, on false overwrite if AVP is single or add to group if AVP is Grouped func messageSetAVPsWithPath(m *diam.Message, pathStr []string, - avpValStr string, appnd bool, tmz string) (err error) { + avpValStr string, newBranch bool, tmz string) (err error) { if len(pathStr) == 0 { return errors.New("empty path as AVP filter") } @@ -209,11 +209,11 @@ func messageSetAVPsWithPath(m *diam.Message, pathStr []string, dictAVPs[i] = dictAVP } } - if dictAVPs[len(path)-1].Data.Type == diam.GroupedAVPType { + lastAVPIdx := len(path) - 1 + if dictAVPs[lastAVPIdx].Data.Type == diam.GroupedAVPType { return errors.New("last AVP in path cannot be GroupedAVP") } var msgAVP *diam.AVP // Keep a reference here towards last AVP - lastAVPIdx := len(path) - 1 for i := lastAVPIdx; i >= 0; i-- { var typeVal datatype.Type if i == lastAVPIdx { @@ -225,7 +225,7 @@ func messageSetAVPsWithPath(m *diam.Message, pathStr []string, AVP: []*diam.AVP{msgAVP}} } newMsgAVP := diam.NewAVP(dictAVPs[i].Code, avp.Mbit, dictAVPs[i].VendorID, typeVal) // FixMe: maybe Mbit with dictionary one - if i == lastAVPIdx-1 && !appnd { // last AVP needs to be appended in group + if i == lastAVPIdx-1 && !newBranch { avps, err := m.FindAVPsWithPath(path[:lastAVPIdx], dict.UndefinedVendorID) if err != nil { return err @@ -239,7 +239,7 @@ func messageSetAVPsWithPath(m *diam.Message, pathStr []string, } msgAVP = newMsgAVP } - if !appnd { // Not group AVP, replace the previous set one with this one + if !newBranch { // Not group AVP, replace the previous set one with this one avps, err := m.FindAVPsWithPath(path, dict.UndefinedVendorID) if err != nil { return err @@ -302,11 +302,14 @@ func (dP *diameterDP) FieldAsString(fldPath []string) (data string, err error) { // FieldAsInterface is part of engine.DataProvider interface func (dP *diameterDP) FieldAsInterface(fldPath []string) (data interface{}, err error) { - if data, err = dP.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return nil, err + if data, err = dP.cache.FieldAsInterface(fldPath); err != nil { + if err != utils.ErrNotFound { // item found in cache + return nil, err + } + err = nil // cancel previous err + } else { + return // data was found in cache } - err = nil // cancel previous err // lastPath can contain selector inside lastPath := fldPath[len(fldPath)-1] var slctrStr string diff --git a/agents/libdiam_test.go b/agents/libdiam_test.go index 9949d998d..151bea642 100644 --- a/agents/libdiam_test.go +++ b/agents/libdiam_test.go @@ -19,6 +19,7 @@ along with this program. If not, see package agents import ( + "reflect" "testing" "github.com/fiorix/go-diameter/diam" @@ -103,3 +104,76 @@ func TestDPFieldAsInterface(t *testing.T) { t.Errorf("Expecting: %v, received: %v", eOut, out) } } + +func TestMessageSetAVPsWithPath(t *testing.T) { + eMessage := diam.NewRequest(diam.CreditControl, 4, nil) + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00001")) + m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4, + eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil) + if err := messageSetAVPsWithPath(m, + []string{"Session-Id"}, "simuhuawei;1449573472;00001", + false, "UTC"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMessage, m) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + // create same attribute twice + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00002")) + if err := messageSetAVPsWithPath(m, + []string{"Session-Id"}, "simuhuawei;1449573472;00002", + true, "UTC"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMessage.AVP, m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + // overwrite of previous attribute + eMessage = diam.NewRequest(diam.CreditControl, 4, nil) + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00001")) + eMessage.NewAVP("Session-Id", avp.Mbit, 0, + datatype.UTF8String("simuhuawei;1449573472;00003")) + if err := messageSetAVPsWithPath(m, + []string{"Session-Id"}, "simuhuawei;1449573472;00003", + false, "UTC"); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(eMessage.AVP, m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + // adding a groupped AVP + eMessage.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(0)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1001")), + }}) + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Type"}, "0", + false, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Data"}, "1001", + false, "UTC"); err != nil { + t.Error(err) + } else if len(eMessage.AVP) != len(m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } + eMessage.NewAVP(avp.SubscriptionID, avp.Mbit, 0, &diam.GroupedAVP{ + AVP: []*diam.AVP{ + diam.NewAVP(avp.SubscriptionIDType, avp.Mbit, 0, datatype.Enumerated(1)), + diam.NewAVP(avp.SubscriptionIDData, avp.Mbit, 0, datatype.UTF8String("1002")), + }}) + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Type"}, "1", + true, "UTC"); err != nil { + t.Error(err) + } + if err := messageSetAVPsWithPath(m, + []string{"Subscription-Id", "Subscription-Id-Data"}, "1002", + false, "UTC"); err != nil { + t.Error(err) + } else if len(eMessage.AVP) != len(m.AVP) { + t.Errorf("Expecting: %+v, received: %+v", eMessage, m) + } +} diff --git a/agents/libhttpagent.go b/agents/libhttpagent.go index 4b06c6abb..ff2c47dbe 100644 --- a/agents/libhttpagent.go +++ b/agents/libhttpagent.go @@ -62,11 +62,14 @@ func (hU *httpUrlDP) FieldAsInterface(fldPath []string) (data interface{}, err e if len(fldPath) != 1 { return nil, utils.ErrNotFound } - if data, err = hU.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return + if data, err = hU.cache.FieldAsInterface(fldPath); err != nil { + if err != utils.ErrNotFound { // item found in cache + return + } + err = nil // cancel previous err + } else { + return // data found in cache } - err = nil // cancel previous err data = hU.req.FormValue(fldPath[0]) hU.cache.Set(fldPath, data, false) return diff --git a/agents/librad.go b/agents/librad.go index a133a22cd..33eabfd90 100644 --- a/agents/librad.go +++ b/agents/librad.go @@ -158,11 +158,14 @@ func (pk *radiusDP) FieldAsInterface(fldPath []string) (data interface{}, err er if len(fldPath) != 1 { return nil, utils.ErrNotFound } - if data, err = pk.cache.FieldAsInterface(fldPath); err == nil || - err != utils.ErrNotFound { // item found in cache - return + if data, err = pk.cache.FieldAsInterface(fldPath); err != nil { + if err != utils.ErrNotFound { // item found in cache + return + } + err = nil // cancel previous err + } else { + return // data found in cache } - err = nil // cancel previous err if len(pk.req.AttributesWithName(fldPath[0], "")) != 0 { data = pk.req.AttributesWithName(fldPath[0], "")[0].GetStringValue() } diff --git a/data/conf/samples/diamagent/dryrun.json b/data/conf/samples/diamagent/dryrun.json index 28b03dba2..452149651 100644 --- a/data/conf/samples/diamagent/dryrun.json +++ b/data/conf/samples/diamagent/dryrun.json @@ -5,7 +5,7 @@ { "id": "dryrun1", - "filters": ["*string:*vars.*cmd:CCR", "*string:*req.Service-Context-Id:TestDiamITDryRun"], + "filters": ["*string:*vars.*cmd:CCR", "*string:*req.Service-Context-Id:TestDiamItDryRun"], "flags": ["*dryrun"], "request_fields":[ {"tag": "TOR", "field_id": "ToR", "type": "*constant", "value": "*sms"}, @@ -16,7 +16,7 @@ ], "reply_fields":[ {"tag": "CCATemplate", "type": "*template", "value": "*cca"}, - {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "300"}, + {"tag": "ResultCode", "field_id": "Result-Code", "type": "*constant", "value": "2002"}, ], }, ],