//go:build integration // +build integration /* 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 Affero 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see */ package dispatchers import ( "reflect" "testing" "github.com/cgrates/birpc/context" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/utils" ) var sTestsDspRes = []func(t *testing.T){ testDspResPingFailover, testDspResPing, testDspResTestAuthKey, testDspResTestAuthKey2, testDspResTestAuthKey3, } // Test start here func TestDspResourceSIT(t *testing.T) { var config1, config2, config3 string switch *utils.DBType { case utils.MetaInternal: t.SkipNow() case utils.MetaMySQL: config1 = "all_mysql" config2 = "all2_mysql" config3 = "dispatchers_mysql" case utils.MetaMongo: config1 = "all_mongo" config2 = "all2_mongo" config3 = "dispatchers_mongo" case utils.MetaPostgres: t.SkipNow() default: t.Fatal("Unknown Database type") } dispDIR := "dispatchers" if *utils.Encoding == utils.MetaGOB { dispDIR += "_gob" } testDsp(t, sTestsDspRes, "TestDspResourceS", config1, config2, config3, "tutorial", "oldtutorial", dispDIR) } func testDspResPingFailover(t *testing.T) { var reply string if err := allEngine.RPC.Call(context.Background(), utils.ResourceSv1Ping, new(utils.CGREvent), &reply); err != nil { t.Error(err) } else if reply != utils.Pong { t.Errorf("Received: %s", reply) } ev := utils.CGREvent{ Tenant: "cgrates.org", APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1Ping, &ev, &reply); err != nil { t.Error(err) } else if reply != utils.Pong { t.Errorf("Received: %s", reply) } allEngine.stopEngine(t) if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1Ping, &ev, &reply); err != nil { t.Error(err) } else if reply != utils.Pong { t.Errorf("Received: %s", reply) } allEngine2.stopEngine(t) if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1Ping, &ev, &reply); err == nil { t.Errorf("Expected error but received %v and reply %v\n", err, reply) } allEngine.startEngine(t) allEngine2.startEngine(t) } func testDspResPing(t *testing.T) { var reply string if err := allEngine.RPC.Call(context.Background(), utils.ResourceSv1Ping, new(utils.CGREvent), &reply); err != nil { t.Error(err) } else if reply != utils.Pong { t.Errorf("Received: %s", reply) } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1Ping, &utils.CGREvent{ Tenant: "cgrates.org", APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", }, }, &reply); err != nil { t.Error(err) } else if reply != utils.Pong { t.Errorf("Received: %s", reply) } } func testDspResTestAuthKey(t *testing.T) { var rs *engine.Resources args := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]any{ utils.AccountField: "1001", utils.Destination: "1002", }, APIOpts: map[string]any{ utils.OptsAPIKey: "12345", utils.OptsResourcesUsageID: utils.UUIDSha1Prefix(), }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1GetResourcesForEvent, args, &rs); err == nil || err.Error() != utils.ErrUnauthorizedApi.Error() { t.Error(err) } } func testDspResTestAuthKey2(t *testing.T) { var rs *engine.Resources args := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]any{ utils.AccountField: "1001", utils.Destination: "1002", }, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", utils.OptsResourcesUsageID: utils.UUIDSha1Prefix(), }, } eRs := &engine.Resources{ &engine.Resource{ Tenant: "cgrates.org", ID: "ResGroup1", Usages: map[string]*engine.ResourceUsage{}, }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil { t.Error(err) } else if !reflect.DeepEqual(eRs, rs) { t.Errorf("Expecting : %+v, received: %+v", utils.ToJSON(eRs), utils.ToJSON(rs)) } } func testDspResTestAuthKey3(t *testing.T) { // first event matching Resource1 var reply string ev := &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]any{ "Account": "1002", "Subject": "1001", "Destination": "1002"}, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e51", utils.OptsResourcesUnits: 1, }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1AllocateResources, ev, &reply); err != nil { t.Error(err) } eAllocationMsg := "ResGroup1" if reply != eAllocationMsg { t.Errorf("Expecting: %+v, received: %+v", eAllocationMsg, reply) } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1AuthorizeResources, &ev, &reply); err != nil { t.Error(err) } else if reply != eAllocationMsg { // already 3 usages active before allow call, we should have now more than allowed t.Errorf("Expecting: %+v, received: %+v", eAllocationMsg, reply) } ev = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]any{ "Account": "1002", "Subject": "1001", "Destination": "1002"}, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e61", utils.OptsResourcesUnits: 17, }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1AuthorizeResources, &ev, &reply); err == nil || err.Error() != utils.ErrResourceUnauthorized.Error() { t.Error(err) } // relase the only resource active for Resource1 ev = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]any{ "Account": "1002", "Subject": "1001", "Destination": "1002"}, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e55", }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1ReleaseResources, ev, &reply); err != nil { t.Error(err) } // try reserving with full units for Resource1, case which did not work in previous test // only match Resource1 since we don't want for storing of the resource2 bellow ev = &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.UUIDSha1Prefix(), Event: map[string]any{ "Account": "1002", "Subject": "1001", "Destination": "1002"}, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e61", utils.OptsResourcesUnits: 6, }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1AuthorizeResources, &ev, &reply); err != nil { t.Error(err) } else if reply != "ResGroup1" { t.Error("Unexpected reply returned", reply) } var rs *engine.Resources args := &utils.CGREvent{ Tenant: "cgrates.org", ID: "Event5", Event: map[string]any{ "Account": "1002", "Subject": "1001", "Destination": "1002"}, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", utils.OptsResourcesUsageID: "651a8db2-4f67-4cf8-b622-169e8a482e61", }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1GetResourcesForEvent, args, &rs); err != nil { t.Error(err) } else if len(*rs) != 1 { t.Errorf("Resources: %+v", utils.ToJSON(rs)) } if rs == nil { t.Errorf("Expecting rs to not be nil") // rs shoud not be nil so exit function // to avoid nil segmentation fault; // if this happens try to run this test manualy return } // make sure Resource1 have no more active resources for _, r := range *rs { if r.ID == "ResGroup1" && (len(r.Usages) != 1 || len(r.TTLIdx) != 0) { t.Errorf("Unexpected resource: %+v", utils.ToJSON(r)) } } var r *engine.Resource argsGetResource := &utils.TenantIDWithAPIOpts{ TenantID: &utils.TenantID{Tenant: "cgrates.org", ID: "ResGroup1"}, APIOpts: map[string]any{ utils.OptsAPIKey: "res12345", }, } if err := dispEngine.RPC.Call(context.Background(), utils.ResourceSv1GetResource, argsGetResource, &r); err != nil { t.Fatal(err) } // make sure Resource1 have no more active resources if r.ID == "ResGroup1" && (len(r.Usages) != 1 || len(r.TTLIdx) != 0) { t.Errorf("Unexpected resource: %+v", utils.ToJSON(r)) } }