/*
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 agents

import (
	"net"
	"os"
	"path/filepath"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/cgrates/cgrates/config"
	"github.com/cgrates/cgrates/engine"
	"github.com/cgrates/cgrates/utils"
	"github.com/cgrates/go-diameter/diam"
	"github.com/cgrates/go-diameter/diam/avp"
	"github.com/cgrates/go-diameter/diam/datatype"
)

func TestLibDiamDPFieldAsInterface(t *testing.T) {
	m := diam.NewRequest(diam.CreditControl, 4, nil)
	m.NewAVP("Session-Id", avp.Mbit, 0, datatype.UTF8String("simuhuawei;1449573472;00002"))
	m.NewAVP(avp.RequestedServiceUnit, avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(avp.CCMoney, avp.Mbit, 0, &diam.GroupedAVP{
				AVP: []*diam.AVP{
					diam.NewAVP(avp.UnitValue, avp.Mbit, 0, &diam.GroupedAVP{
						AVP: []*diam.AVP{
							diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)),
							diam.NewAVP(avp.Exponent, avp.Mbit, 0, datatype.Integer32(-5)),
						},
					}),
					diam.NewAVP(avp.CurrencyCode, avp.Mbit, 0, datatype.Unsigned32(33)),
				},
			}),
		},
	})
	m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(0)),             // Subscription-Id-Type
			diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("33708000003")), // Subscription-Id-Data
			diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(10000)),
		}})
	m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(1)),              // Subscription-Id-Type
			diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208708000003")), // Subscription-Id-Data
			diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(20000)),
		}})
	m.NewAVP("Subscription-Id", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(2)),              // Subscription-Id-Type
			diam.NewAVP(444, avp.Mbit, 0, datatype.UTF8String("208708000004")), // Subscription-Id-Data
			diam.NewAVP(avp.ValueDigits, avp.Mbit, 0, datatype.Integer64(20000)),
		}})

	dP := newDADataProvider(nil, m)
	eOut := any("simuhuawei;1449573472;00002")
	if out, err := dP.FieldAsInterface([]string{"Session-Id"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	eOut = any(int64(10000))
	if out, err := dP.FieldAsInterface([]string{"Requested-Service-Unit", "CC-Money", "Unit-Value", "Value-Digits"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	eOut = any("208708000003") // with filter on second group item
	if out, err := dP.FieldAsInterface([]string{"Subscription-Id",
		"Subscription-Id-Data[1]"}); err != nil { // on index
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	if out, err := dP.FieldAsInterface([]string{"Subscription-Id",
		"Subscription-Id-Data[~Subscription-Id-Type(1)]"}); err != nil { // on filter
		t.Error(err)
	} else if out != eOut { // can be any result since both entries are matching single filter
		t.Errorf("expecting: %v, received: %v", eOut, out)
	}
	eOut = any("208708000004")
	if out, err := dP.FieldAsInterface([]string{"Subscription-Id",
		"Subscription-Id-Data[~Subscription-Id-Type(2)|~Value-Digits(20000)]"}); err != nil { // on multiple filter
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	eOut = any("33708000003")
	if out, err := dP.FieldAsInterface([]string{"Subscription-Id", "Subscription-Id-Data"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
}

func TestLibDiamMessageSetAVPsWithPath(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)
	}
}

func TestLibDiamMessageSetAVPsWithPath2(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(65000)),
		}})
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(100)),
		}})
	m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)
	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Rating-Group"}, "65000",
		true, "UTC"); err != nil {
		t.Error(err)
	}
	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Rating-Group"}, "100",
		true, "UTC"); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage, m) {
		t.Errorf("Expecting: %+v, received: %+v", eMessage, m)
	}
}

func TestLibDiamMessageSetAVPsWithPath3(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(449, avp.Mbit, 0, datatype.Enumerated(1)), // 449 code for Final-Unit-Action
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), //435 code for Redirect-Server-Address
						},
					},
					),
				},
			},
			),
		},
	},
	)
	m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Final-Unit-Action"}, "1",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}, "2",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}, "http://172.10.88.88/",
		false, "UTC"); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage.String(), m.String()) {
		t.Errorf("Expecting: %+v \n, received: %+v \n", eMessage, m)
	}
}

func TestLibDiamMessageSetAVPsWithPath4(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), //435 code for Redirect-Server-Address
						},
					},
					),
					diam.NewAVP(449, avp.Mbit, 0, datatype.Enumerated(1)), // 449 code for Final-Unit-Action
				},
			},
			),
		},
	},
	)
	m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}, "2",
		false, "UTC"); err != nil {
		t.Error(err)
	}
	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Final-Unit-Action"}, "1",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}, "http://172.10.88.88/",
		false, "UTC"); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage.String(), m.String()) {
		t.Errorf("Expecting: %+v \n, received: %+v \n", eMessage, m)
	}
}

func TestLibDiamMessageSetAVPsWithPath5(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(1)),
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), //435 code for Redirect-Server-Address
						},
					},
					),
					diam.NewAVP(449, avp.Mbit, 0, datatype.Enumerated(1)), // 449 code for Final-Unit-Action
				},
			},
			),
			diam.NewAVP(439, avp.Mbit, 0, datatype.Unsigned32(1)),
		},
	},
	)
	m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Tariff-Change-Usage"}, "1",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}, "2",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Service-Identifier"}, "1",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}, "http://172.10.88.88/",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if err := messageSetAVPsWithPath(m,
		[]string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Final-Unit-Action"}, "1",
		false, "UTC"); err != nil {
		t.Error(err)
	}

	if !reflect.DeepEqual(eMessage.String(), m.String()) {
		t.Errorf("Expected %s, received %s", utils.ToJSON(eMessage), utils.ToJSON(m))
		// t.Errorf("Expecting: %+v \n, received: %+v \n", eMessage, m)
	}
}

// In case we send -1 as CC-Time we get error from go-diameter :
// " strconv.ParseUint: parsing "-1": invalid syntax "
// and crashed
/*
func TestMessageSetAVPsWithPath6(t *testing.T) {
	m := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)

	if err := messageSetAVPsWithPath(m,
		[]string{"Granted-Service-Unit", "CC-Time"}, "-1",
		false, "UTC"); err != nil {
		t.Error(err)
	}
}
*/

func TestLibDiamDisectDiamListen(t *testing.T) {
	expIPs := []net.IP{net.ParseIP("192.168.56.203"), net.ParseIP("192.168.57.203")}
	rvc := disectDiamListen("192.168.56.203/192.168.57.203:3869")
	if !reflect.DeepEqual(expIPs, rvc) {
		t.Errorf("Expecting: %+v \n, received: %+v \n ", expIPs, rvc)
	}
	expIPs = []net.IP{net.ParseIP("192.168.56.203")}
	rvc = disectDiamListen("192.168.56.203:3869")
	if !reflect.DeepEqual(expIPs, rvc) {
		t.Errorf("Expecting: %+v \n, received: %+v \n ", expIPs, rvc)
	}
	expIPs = []net.IP{}
	rvc = disectDiamListen(":3869")
	if len(rvc) != 0 {
		t.Errorf("Expecting: %+v \n, received: %+q \n ", expIPs, rvc)
	}

}

func TestLibDiamUpdateDiamMsgFromNavMap1(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), // 435 code for Redirect-Server-Address
						},
					},
					),
				},
			},
			),
			diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(2)), // 452 code for Redirect-Address-Type
		},
	},
	)

	m2 := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)
	m2.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
			},
			),
		},
	},
	)

	nM := utils.NewOrderedNavigableMap()
	path := []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}
	itm := &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Tariff-Change-Usage"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: "http://172.10.88.88/",
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	if err := updateDiamMsgFromNavMap(m2, nM, ""); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage.String(), m2.String()) {
		t.Errorf("Expected %s, received %s", utils.ToJSON(eMessage), utils.ToJSON(m2))
	}
}

func TestLibDiamUpdateDiamMsgFromNavMap2(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)), // 433 code for Redirect-Address-Type
						},
					},
					),
				},
			},
			),
			diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(2)), // 452 code for Redirect-Address-Type
		},
	},
	)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), // 435 code for Redirect-Server-Address
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
						},
					},
					),
				},
			},
			),
		},
	},
	)

	m2 := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)
	m2.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
			},
			),
		},
	},
	)

	nM := utils.NewOrderedNavigableMap()
	path := []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}
	itm := &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Tariff-Change-Usage"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data:      "http://172.10.88.88/",
		NewBranch: true,
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.Append(
		&utils.FullPath{
			Path:      strings.Join(path, utils.NestingSep),
			PathSlice: []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"},
		}, itm.Value)
	if err := updateDiamMsgFromNavMap(m2, nM, ""); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage.String(), m2.String()) {
		t.Errorf("Expected %s, received %s", utils.ToJSON(eMessage), utils.ToJSON(m2))
	}
}

func TestLibDiamUpdateDiamMsgFromNavMap3(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(449, avp.Mbit, 0, datatype.Enumerated(1)), // 449 code for Final-Unit-Action
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), // 435 code for Redirect-Server-Address
						},
					},
					),
				},
			},
			),
			diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(2)), // 452 code for Redirect-Address-Type
		},
	},
	)

	m2 := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)

	nM := utils.NewOrderedNavigableMap()

	path := []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Final-Unit-Action"}
	itm := &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(1),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Tariff-Change-Usage"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: "http://172.10.88.88/",
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	if err := updateDiamMsgFromNavMap(m2, nM, ""); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage.String(), m2.String()) {
		t.Errorf("Expected %s, received %s", utils.ToJSON(eMessage), utils.ToJSON(m2))
	}
}

func TestLibDiamUpdateDiamMsgFromNavMap4(t *testing.T) {
	eMessage := diam.NewRequest(diam.CreditControl, 4, nil)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(449, avp.Mbit, 0, datatype.Enumerated(1)), // 449 code for Final-Unit-Action
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(433, avp.Mbit, 0, datatype.Enumerated(2)),                      // 433 code for Redirect-Address-Type
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), // 435 code for Redirect-Server-Address
						},
					},
					),
				},
			},
			),
			diam.NewAVP(452, avp.Mbit, 0, datatype.Enumerated(2)), // 452 code for Redirect-Address-Type
		},
	},
	)
	eMessage.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(430, avp.Mbit, 0, &diam.GroupedAVP{ // 430 code for Final-Unit-Indication
				AVP: []*diam.AVP{
					diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
						AVP: []*diam.AVP{
							diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), // 435 code for Redirect-Server-Address
						},
					},
					),
				},
			},
			),
		},
	},
	)
	eMessage.NewAVP("Granted-Service-Unit", avp.Mbit, 0, &diam.GroupedAVP{ // 431 code for Granted-Service-Unit
		AVP: []*diam.AVP{
			diam.NewAVP(420, avp.Mbit, 0, datatype.Unsigned32(10)), // 420 code for CC-Time
		},
	},
	)

	m2 := diam.NewMessage(diam.CreditControl, diam.RequestFlag, 4,
		eMessage.Header.HopByHopID, eMessage.Header.EndToEndID, nil)

	nM := utils.NewOrderedNavigableMap()

	path := []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Final-Unit-Action"}
	itm := &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(1),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Address-Type"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Tariff-Change-Usage"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Enumerated(2),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: "http://172.10.88.88/",
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	path = []string{"Multiple-Services-Credit-Control", "Final-Unit-Indication", "Redirect-Server", "Redirect-Server-Address"}
	itm2 := &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data:      "http://172.10.88.88/",
		NewBranch: true,
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm, itm2})

	path = []string{"Granted-Service-Unit", "CC-Time"}
	itm = &utils.DataNode{Type: utils.NMDataType, Value: &utils.DataLeaf{
		Data: datatype.Unsigned32(10),
	}}
	nM.SetAsSlice(&utils.FullPath{
		Path:      strings.Join(path, utils.NestingSep),
		PathSlice: path,
	}, []*utils.DataNode{itm})

	if err := updateDiamMsgFromNavMap(m2, nM, ""); err != nil {
		t.Error(err)
	}
	if !reflect.DeepEqual(eMessage.String(), m2.String()) {
		t.Errorf("Expected %s, received %s", utils.ToJSON(eMessage), utils.ToJSON(m2))
	}
}

func TestLibDiamAVPAsIface(t *testing.T) {
	args := diam.NewAVP(435, avp.Mbit, 0, datatype.Address("127.0.0.1"))
	var exp any = net.IP([]byte("127.0.0.1"))
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(435, avp.Mbit, 0, datatype.DiameterIdentity("diam1"))
	exp = "diam1"
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(435, avp.Mbit, 0, datatype.DiameterURI("http://172.10.88.88/"))
	exp = "http://172.10.88.88/"
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Enumerated(10))
	exp = int32(10)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Float32(10.25))
	exp = float32(10.25)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Float64(10.25))
	exp = float64(10.25)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.IPFilterRule("fltr1"))
	exp = "fltr1"
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(435, avp.Mbit, 0, datatype.IPv4("127.0.0.1"))
	exp = net.IP([]byte("127.0.0.1"))
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Integer32(10))
	exp = int32(10)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Integer64(10))
	exp = int64(10)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.OctetString("diam1"))
	exp = "diam1"
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.QoSFilterRule("diam1"))
	exp = "diam1"
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.UTF8String("diam1"))
	exp = "diam1"
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Unsigned32(10))
	exp = uint32(10)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Unsigned64(10))
	exp = uint64(10)
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	now := time.Now()
	args = diam.NewAVP(450, avp.Mbit, 0, datatype.Time(now))
	exp = now
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	args = diam.NewAVP(434, avp.Mbit, 0, &diam.GroupedAVP{ // 434 code for Redirect-Server
		AVP: []*diam.AVP{
			diam.NewAVP(435, avp.Mbit, 0, datatype.UTF8String("http://172.10.88.88/")), // 435 code for Redirect-Server-Address
		},
	})
	if rply, err := diamAVPAsIface(args); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	args = diam.NewAVP(257, avp.Mbit, 0, datatype.Address("10.170.248.140"))
	exp = net.IP([]byte("10.170.248.140"))
	if rply, err := diamAVPAsIface(args); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}
}

func TestLibDiamNewDiamDataType(t *testing.T) {
	argType := datatype.AddressType
	argVal := "127.0.0.1"
	var exp datatype.Type = datatype.Address(net.ParseIP("127.0.0.1"))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.DiameterIdentityType
	argVal = "diam1"
	exp = datatype.DiameterIdentity("diam1")
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.DiameterURIType
	argVal = "http://172.10.88.88/"
	exp = datatype.DiameterURI("http://172.10.88.88/")
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.EnumeratedType
	argVal = "00"
	exp = datatype.Enumerated(int32(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.Float32Type
	argVal = "00"
	exp = datatype.Float32(float32(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0A"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.Float64Type
	argVal = "00"
	exp = datatype.Float64(float64(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0A"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.IPFilterRuleType
	argVal = "filter1"
	exp = datatype.IPFilterRule("filter1")
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.IPv4Type
	argVal = "127.0.0.1"
	exp = datatype.IPv4(net.ParseIP("127.0.0.1"))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.Integer32Type
	argVal = "00"
	exp = datatype.Integer32(int32(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.Integer64Type
	argVal = "00"
	exp = datatype.Integer64(int64(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.OctetStringType
	argVal = "diam1"
	exp = datatype.OctetString("diam1")
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.QoSFilterRuleType
	argVal = "diam1"
	exp = datatype.QoSFilterRule("diam1")
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.UTF8StringType
	argVal = "diam1"
	exp = datatype.UTF8String("diam1")
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argType = datatype.Unsigned32Type
	argVal = "00"
	exp = datatype.Unsigned32(uint32(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.Unsigned64Type
	argVal = "00"
	exp = datatype.Unsigned64(uint64(0))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	now := time.Now().UTC()

	argType = datatype.TimeType
	argVal = now.String()
	exp = datatype.Time(now)
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}

	argVal = "0.0"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.GroupedType
	argVal = "{}"
	if rply, err := newDiamDataType(argType, argVal, ""); err == nil {
		t.Errorf("Expected err received: err: %v, rply %v", err, rply)
	}

	argType = datatype.AddressType
	argVal = "10.170.248.140"
	exp = datatype.Address(net.ParseIP("10.170.248.140"))
	if rply, err := newDiamDataType(argType, argVal, ""); err != nil {
		t.Error(err)
	} else if !reflect.DeepEqual(exp, rply) {
		t.Errorf("Expected<%T>: %v ,received<%T>: %v ", exp, exp, rply, rply)
	}
}

func TestLibDiamAvpGroupIface(t *testing.T) {
	avps := diam.NewRequest(diam.CreditControl, 4, nil)
	avps.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)),
		}})
	avps.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(99)),
		}})
	dP := newDADataProvider(nil, avps)
	eOut := any(uint32(1))
	if out, err := dP.FieldAsInterface([]string{"Multiple-Services-Credit-Control", "Rating-Group"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	dP.(*diameterDP).cache = utils.MapStorage{}
	if out, err := dP.FieldAsInterface([]string{"Multiple-Services-Credit-Control", "Rating-Group[~Rating-Group(1)]"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	dP.(*diameterDP).cache = utils.MapStorage{}
	eOut = any(uint32(99))
	if out, err := dP.FieldAsInterface([]string{"Multiple-Services-Credit-Control", "Rating-Group[1]"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	dP.(*diameterDP).cache = utils.MapStorage{}
	if out, err := dP.FieldAsInterface([]string{"Multiple-Services-Credit-Control", "Rating-Group[~Rating-Group(99)]"}); err != nil {
		t.Error(err)
	} else if eOut != out {
		t.Errorf("Expecting: %v, received: %v", eOut, out)
	}
	dP.(*diameterDP).cache = utils.MapStorage{}
	if _, err := dP.FieldAsInterface([]string{"Multiple-Services-Credit-Control", "Rating-Group[~Rating-Group(10)]"}); err != utils.ErrNotFound {
		t.Error(err)
	}
}

func TestLibDiamFilterWithDiameterDP(t *testing.T) {
	avps := diam.NewRequest(diam.CreditControl, 4, nil)
	avps.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(1)),
		}})
	avps.NewAVP("Multiple-Services-Credit-Control", avp.Mbit, 0, &diam.GroupedAVP{
		AVP: []*diam.AVP{
			diam.NewAVP(432, avp.Mbit, 0, datatype.Unsigned32(99)),
		}})
	dP := newDADataProvider(nil, avps)
	cfg := config.NewDefaultCGRConfig()
	idb, err := engine.NewInternalDB(nil, nil, true, nil, cfg.DataDbCfg().Items)
	if err != nil {
		t.Error(err)
	}
	dm := engine.NewDataManager(idb,
		config.CgrConfig().CacheCfg(), nil)
	filterS := engine.NewFilterS(cfg, nil, dm)
	agReq := NewAgentRequest(dP, nil, nil, nil, nil, nil, "cgrates.org", "", filterS, nil)

	if pass, err := filterS.Pass("cgrates.org",
		[]string{"*exists:~*req.Multiple-Services-Credit-Control.Rating-Group[~Rating-Group(99)]:"}, agReq); err != nil {
		t.Error(err)
	} else if !pass {
		t.Errorf("Exptected true, received: %+v", pass)
	}

	if pass, err := filterS.Pass("cgrates.org",
		[]string{"*exists:~*req.Multiple-Services-Credit-Control.Rating-Group[~Rating-Group(10)]:"}, agReq); err != nil {
		t.Error(err)
	} else if pass {
		t.Errorf("Exptected false, received: %+v", pass)
	}

	if pass, err := filterS.Pass("cgrates.org",
		[]string{"*string:~*req.Multiple-Services-Credit-Control.Rating-Group[~Rating-Group(10)]:12"}, agReq); err != nil {
		t.Error(err)
	} else if pass {
		t.Errorf("Exptected false, received: %+v", pass)
	}

	if pass, err := filterS.Pass("cgrates.org",
		[]string{"*string:~*req.Multiple-Services-Credit-Control.Rating-Group[~Rating-Group(1)]:1"}, agReq); err != nil {
		t.Error(err)
	} else if !pass {
		t.Errorf("Exptected true, received: %+v", pass)
	}
}

func TestLibDiamHeaderLen(t *testing.T) {
	tests := []struct {
		name    string
		avp     *diam.AVP
		wantLen int
	}{
		{
			name:    "len equals",
			avp:     &diam.AVP{Flags: avp.Vbit},
			wantLen: 12,
		},
		{
			name:    "len not set",
			avp:     &diam.AVP{},
			wantLen: 8,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			gotLen := headerLen(tt.avp)
			if gotLen != tt.wantLen {
				t.Errorf("headerLen() returned %d, want %d", gotLen, tt.wantLen)
			}
		})
	}
}

func TestLibDiamAVPAsString(t *testing.T) {
	testCases := []struct {
		name      string
		dAVP      *diam.AVP
		expected  string
		expectErr bool
	}{
		{
			name:     "IPv4 Address AVP",
			dAVP:     &diam.AVP{Data: datatype.Address(net.IPv4(192, 168, 0, 1))},
			expected: "192.168.0.1",
		},
		{
			name:     "Diameter Identity AVP",
			dAVP:     &diam.AVP{Data: datatype.DiameterIdentity("cgrates.com")},
			expected: "cgrates.com",
		},
		{
			name:     "Time AVP",
			dAVP:     &diam.AVP{Data: datatype.Time(time.Now())},
			expected: time.Now().Format(time.RFC3339),
		},
		{
			name:      "Nil AVP",
			dAVP:      nil,
			expectErr: true,
		},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			result, err := diamAVPAsString(tc.dAVP)
			if tc.expectErr {
				if err == nil {
					t.Error("Expected an error, but got nil")
				}
				return
			}
			if err != nil {
				t.Errorf("Unexpected error: %v", err)
				return
			}
			if result != tc.expected {
				t.Errorf("Expected %q, got %q", tc.expected, result)
			}
		})
	}
}

func TestLibDiamUpdateAVPLength(t *testing.T) {

	testCases := []struct {
		name     string
		avps     []*diam.AVP
		expected int
	}{
		{
			name: "Single AVP without GroupedAVP",
			avps: []*diam.AVP{
				{Length: 10},
				{Length: 20},
				{Length: 15},
			},
			expected: 45,
		},
		{
			name:     "Empty AVP slice",
			avps:     []*diam.AVP{},
			expected: 0,
		},
	}

	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {

			result := updateAVPLength(tc.avps)

			if result != tc.expected {
				t.Errorf("Expected length %d, got %d", tc.expected, result)
			}
		})
	}
}

func TestLibDiamUpdateAVPLengthWithGroupedAVP(t *testing.T) {
	groupedAVP := &diam.AVP{Length: 10}
	avps := []*diam.AVP{
		{Length: 10},
		{
			Data: &diam.GroupedAVP{
				AVP: []*diam.AVP{
					groupedAVP,
					{Length: 0},
				},
			},
		},
		{Length: 15},
	}
	expected := 10 + headerLen(avps[1]) + groupedAVP.Length + avps[2].Length
	result := updateAVPLength(avps)
	if result != expected {
		t.Errorf("Expected length %d, got %d", expected, result)
	}
}

func TestLibDiamDiameterDPString(t *testing.T) {
	msg := &diam.Message{
		Header: &diam.Header{
			Version:       1,
			MessageLength: 20,
			CommandFlags:  2,
			CommandCode:   272,
			ApplicationID: 0,
			HopByHopID:    12345,
			EndToEndID:    67890,
		},
	}
	dp := &diameterDP{
		m: msg,
	}
	result := dp.String()
	expected := msg.String()
	if result != expected {
		t.Errorf("Expected %q, but got %q", expected, result)
	}
}

func TestLibDiamLoadDictionaries(t *testing.T) {
	tempDir := t.TempDir()
	dictsDir := filepath.Join(tempDir, "dicts")
	err := os.Mkdir(dictsDir, os.ModePerm)
	if err != nil {
		t.Fatalf("Failed to create temporary directory: %v", err)
	}
	testCases := []struct {
		name             string
		dictsDir         string
		expectedErrorMsg string
	}{
		{
			name:     "Valid Directory",
			dictsDir: dictsDir,
		},
		{
			name:             "Invalid Directory",
			dictsDir:         filepath.Join(tempDir, "nonexistent", "directory"),
			expectedErrorMsg: "Invalid dictionaries folder",
		},
	}
	for _, tc := range testCases {
		t.Run(tc.name, func(t *testing.T) {
			err := loadDictionaries(tc.dictsDir, "testComponent")
			if tc.expectedErrorMsg == "" && err != nil {
				t.Errorf("Unexpected error: %v", err)
			}
			if tc.expectedErrorMsg != "" && !strings.Contains(err.Error(), tc.expectedErrorMsg) {
				t.Errorf("Expected error containing %q, got: %v", tc.expectedErrorMsg, err)
			}
		})
	}
}
