From 5607db3c50403c90c36b28c6747a60fc3fa71921 Mon Sep 17 00:00:00 2001 From: andronache Date: Tue, 5 Jan 2021 13:20:48 +0200 Subject: [PATCH] Added tpaccountprofiles.go and integration tests in APIer --- apier/v1/tpaccountprofiles.go | 95 ++++++++++ apier/v1/tpaccountprofiles_it_test.go | 249 ++++++++++++++++++++++++++ utils/consts.go | 4 + 3 files changed, 348 insertions(+) create mode 100644 apier/v1/tpaccountprofiles.go create mode 100644 apier/v1/tpaccountprofiles_it_test.go diff --git a/apier/v1/tpaccountprofiles.go b/apier/v1/tpaccountprofiles.go new file mode 100644 index 000000000..b9d487843 --- /dev/null +++ b/apier/v1/tpaccountprofiles.go @@ -0,0 +1,95 @@ +/* +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 +*/ + +package v1 + +import ( + "github.com/cgrates/cgrates/utils" +) + +// SetTPAccountProfile creates a new TPAccountProfile within a tariff plan +func (apierSv1 *APIerSv1) SetTPAccountProfile(attrs *utils.TPAccountProfile, reply *string) error { + if missing := utils.MissingStructFields(attrs, []string{utils.TPid, utils.ID}); len(missing) != 0 { + return utils.NewErrMandatoryIeMissing(missing...) + } + if attrs.Tenant == utils.EmptyString { + attrs.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + } + if err := apierSv1.StorDb.SetTPAccountProfiles([]*utils.TPAccountProfile{attrs}); err != nil { + return utils.NewErrServerError(err) + } + *reply = utils.OK + return nil +} + +// GetTPAccountProfile queries specific TPAccountProfile on tariff plan +func (apierSv1 *APIerSv1) GetTPAccountProfile(attr *utils.TPTntID, reply *utils.TPAccountProfile) error { + if missing := utils.MissingStructFields(attr, []string{utils.TPid, utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + if attr.Tenant == utils.EmptyString { + attr.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + } + spp, err := apierSv1.StorDb.GetTPAccountProfiles(attr.TPid, attr.Tenant, attr.ID) + if err != nil { + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err + } + *reply = *spp[0] + return nil +} + +type AttrGetTPAccountProfileIDs struct { + TPid string // Tariff plan id + utils.PaginatorWithSearch +} + +// GetTPRouteProfileIDs queries TPAccountProfiles identities on specific tariff plan. +func (apierSv1 *APIerSv1) GetTPAccountProfileIDs(attrs *AttrGetTPAccountProfileIDs, reply *[]string) error { + if missing := utils.MissingStructFields(attrs, []string{utils.TPid}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + ids, err := apierSv1.StorDb.GetTpTableIds(attrs.TPid, utils.TBLTPAccountProfiles, + utils.TPDistinctIds{"tenant", "id"}, nil, &attrs.PaginatorWithSearch) + if err != nil { + if err.Error() != utils.ErrNotFound.Error() { + err = utils.NewErrServerError(err) + } + return err + } + *reply = ids + return nil +} + +// RemoveTPAccountProfile removes specific TPAccountProfile on Tariff plan +func (apierSv1 *APIerSv1) RemoveTPAccountProfile(attrs *utils.TPTntID, reply *string) error { + if missing := utils.MissingStructFields(attrs, []string{utils.TPid, utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + if attrs.Tenant == utils.EmptyString { + attrs.Tenant = apierSv1.Config.GeneralCfg().DefaultTenant + } + if err := apierSv1.StorDb.RemTpData(utils.TBLTPAccountProfiles, attrs.TPid, + map[string]string{utils.TenantCfg: attrs.Tenant, utils.IDCfg: attrs.ID}); err != nil { + return utils.NewErrServerError(err) + } + *reply = utils.OK + return nil +} diff --git a/apier/v1/tpaccountprofiles_it_test.go b/apier/v1/tpaccountprofiles_it_test.go new file mode 100644 index 000000000..99f84b9c5 --- /dev/null +++ b/apier/v1/tpaccountprofiles_it_test.go @@ -0,0 +1,249 @@ +// +build offline + +/* +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 +*/ + +package v1 + +import ( + "net/rpc" + "net/rpc/jsonrpc" + "path" + "reflect" + "sort" + "strings" + "testing" + + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +var ( + tpAcctPrfCfgPath string + tpAcctPrfCfg *config.CGRConfig + tpAcctPrfRPC *rpc.Client + tpAcctPrfDataDir = "/usr/share/cgrates" + tpAcctPrf *utils.TPAccountProfile + tpAcctPrfDelay int + tpAcctPrfConfigDIR string //run tests for specific configuration +) + +var sTestsTPAcctPrf = []func(t *testing.T){ + testTPAcctPrfInitCfg, + testTPAcctPrfResetStorDb, + testTPAcctPrfStartEngine, + testTPAcctPrfRPCConn, + testTPAcctPrfGetTPAcctPrfBeforeSet, + testTPAcctPrfSetTPAcctPrf, + testTPAcctPrfGetTPAcctPrfAfterSet, + testTPAcctPrfGetTPAcctPrfIDs, + testTPAcctPrfUpdateTPAcctBal, + testTPAcctPrfGetTPAcctBalAfterUpdate, + testTPAcctPrfRemTPAcctPrf, + testTPAcctPrfGetTPAcctPrfAfterRemove, + testTPAcctPrfKillEngine, +} + +//Test start here +func TestTPAcctPrfIT(t *testing.T) { + switch *dbType { + case utils.MetaInternal: + tpAcctPrfConfigDIR = "tutinternal" + case utils.MetaMySQL: + tpAcctPrfConfigDIR = "tutmysql" + case utils.MetaMongo: + tpAcctPrfConfigDIR = "tutmongo" + case utils.MetaPostgres: + t.SkipNow() + default: + t.Fatal("Unknown Database type") + } + for _, stest := range sTestsTPAcctPrf { + t.Run(tpAcctPrfConfigDIR, stest) + } +} + +func testTPAcctPrfInitCfg(t *testing.T) { + var err error + tpAcctPrfCfgPath = path.Join(tpAcctPrfDataDir, "conf", "samples", tpAcctPrfConfigDIR) + tpAcctPrfCfg, err = config.NewCGRConfigFromPath(tpAcctPrfCfgPath) + if err != nil { + t.Error(err) + } + tpAcctPrfDelay = 1000 +} + +// Wipe out the cdr database +func testTPAcctPrfResetStorDb(t *testing.T) { + if err := engine.InitStorDb(tpAcctPrfCfg); err != nil { + t.Fatal(err) + } +} + +// Start CGR Engine +func testTPAcctPrfStartEngine(t *testing.T) { + if _, err := engine.StopStartEngine(tpAcctPrfCfgPath, tpAcctPrfDelay); err != nil { + t.Fatal(err) + } +} + +// Connect rpc client to rater +func testTPAcctPrfRPCConn(t *testing.T) { + var err error + tpAcctPrfRPC, err = jsonrpc.Dial(utils.TCP, tpAcctPrfCfg.ListenCfg().RPCJSONListen) // We connect over JSON so we can also troubleshoot if needed + if err != nil { + t.Fatal(err) + } +} + +func testTPAcctPrfGetTPAcctPrfBeforeSet(t *testing.T) { + var reply *utils.TPAccountProfile + if err := tpAcctPrfRPC.Call(utils.APIerSv1GetTPAccountProfile, + &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "Attr1"}, &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPAcctPrfSetTPAcctPrf(t *testing.T) { + tpAcctPrf = &utils.TPAccountProfile{ + TPid: "TP1", + Tenant: "cgrates.org", + ID: "1001", + Weight: 20, + Balances: []*utils.TPAccountBalance{ + &utils.TPAccountBalance{ + ID: "MonetaryBalance", + FilterIDs: []string{}, + Weight: 10, + Type: utils.MONETARY, + Value: 14, + }, + }, + ThresholdIDs: []string{utils.META_NONE}, + } + sort.Strings(tpAcctPrf.FilterIDs) + var result string + if err := tpAcctPrfRPC.Call(utils.APIerSv1SetTPAccountProfile, tpAcctPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPAcctPrfGetTPAcctPrfAfterSet(t *testing.T) { + var reply *utils.TPAccountProfile + if err := tpAcctPrfRPC.Call(utils.APIerSv1GetTPAccountProfile, + &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "1001"}, &reply); err != nil { + t.Fatal(err) + } + sort.Strings(reply.FilterIDs) + if !reflect.DeepEqual(tpAcctPrf, reply) { + t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(tpAcctPrf), utils.ToJSON(reply)) + } +} + +func testTPAcctPrfGetTPAcctPrfIDs(t *testing.T) { + var result []string + expectedTPID := []string{"cgrates.org:1001"} + if err := tpAcctPrfRPC.Call(utils.APIerSv1GetTPAccountProfileIDs, + &AttrGetTPAccountProfileIDs{TPid: "TP1"}, &result); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(expectedTPID, result) { + t.Errorf("Expecting: %+v, received: %+v", expectedTPID, result) + } +} + +func testTPAcctPrfUpdateTPAcctBal(t *testing.T) { + tpAcctPrf.Balances = []*utils.TPAccountBalance{ + { + ID: "MonetaryBalance2", + FilterIDs: []string{}, + Weight: 12, + Type: utils.MONETARY, + Value: 16, + }, + } + var result string + if err := tpAcctPrfRPC.Call(utils.APIerSv1SetTPAccountProfile, tpAcctPrf, &result); err != nil { + t.Error(err) + } else if result != utils.OK { + t.Error("Unexpected reply returned", result) + } +} + +func testTPAcctPrfGetTPAcctBalAfterUpdate(t *testing.T) { + var reply *utils.TPAccountProfile + revTPAcctPrf := &utils.TPAccountProfile{ + TPid: "TP1", + Tenant: "cgrates.org", + ID: "1001", + Weight: 20, + Balances: []*utils.TPAccountBalance{ + &utils.TPAccountBalance{ + ID: "MonetaryBalance2", + FilterIDs: []string{}, + Weight: 12, + Type: utils.MONETARY, + Value: 16, + }, + }, + ThresholdIDs: []string{utils.META_NONE}, + } + sort.Strings(revTPAcctPrf.FilterIDs) + sort.Slice(revTPAcctPrf.Balances, func(i, j int) bool { + return strings.Compare(revTPAcctPrf.Balances[i].Type, revTPAcctPrf.Balances[j].Type) == -1 + }) + if err := tpAcctPrfRPC.Call(utils.APIerSv1GetTPAccountProfile, + &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "1001"}, &reply); err != nil { + t.Fatal(err) + } + sort.Strings(reply.FilterIDs) + sort.Slice(reply.Balances, func(i, j int) bool { + return strings.Compare(reply.Balances[i].Type, reply.Balances[j].Type) == -1 + }) + if !reflect.DeepEqual(tpAcctPrf, reply) && !reflect.DeepEqual(revTPAcctPrf, reply) { + t.Errorf("Expecting : %+v, \n received: %+v", utils.ToJSON(tpAcctPrf), utils.ToJSON(reply)) + } +} + +func testTPAcctPrfRemTPAcctPrf(t *testing.T) { + var resp string + if err := tpAcctPrfRPC.Call(utils.APIerSv1RemoveTPAccountProfile, + &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "1001"}, + &resp); err != nil { + t.Error(err) + } else if resp != utils.OK { + t.Error("Unexpected reply returned", resp) + } +} + +func testTPAcctPrfGetTPAcctPrfAfterRemove(t *testing.T) { + var reply *utils.TPAccountProfile + if err := tpAcctPrfRPC.Call(utils.APIerSv1GetTPAccountProfile, + &utils.TPTntID{TPid: "TP1", Tenant: "cgrates.org", ID: "1001"}, + &reply); err == nil || err.Error() != utils.ErrNotFound.Error() { + t.Error(err) + } +} + +func testTPAcctPrfKillEngine(t *testing.T) { + if err := engine.KillEngine(tpAcctPrfDelay); err != nil { + t.Error(err) + } +} diff --git a/utils/consts.go b/utils/consts.go index f65ba6233..6b8dbec40 100755 --- a/utils/consts.go +++ b/utils/consts.go @@ -1470,6 +1470,10 @@ const ( APIerSv1SetTPRateProfile = "APIerSv1.SetTPRateProfile" APIerSv1GetTPRateProfileIds = "APIerSv1.GetTPRateProfileIds" APIerSv1RemoveTPRateProfile = "APIerSv1.RemoveTPRateProfile" + APIerSv1GetTPAccountProfile = "APIerSv1.GetTPAccountProfile" + APIerSv1SetTPAccountProfile = "APIerSv1.SetTPAccountProfile" + APIerSv1RemoveTPAccountProfile = "APIerSv1.RemoveTPAccountProfile" + APIerSv1GetTPAccountProfileIDs = "APIerSv1.GetTPAccountProfileIDs" ) // APIerSv1 TP APIs