diff --git a/apis/actions_it_test.go b/apis/actions_it_test.go
new file mode 100644
index 000000000..227fd84c6
--- /dev/null
+++ b/apis/actions_it_test.go
@@ -0,0 +1,301 @@
+// +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 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 apis
+
+import (
+ "path"
+ "reflect"
+ "testing"
+
+ "github.com/cgrates/birpc"
+ "github.com/cgrates/birpc/context"
+ "github.com/cgrates/cgrates/config"
+ "github.com/cgrates/cgrates/engine"
+ "github.com/cgrates/cgrates/utils"
+)
+
+var (
+ // actSrv *httptest.Server
+ // actBody []byte
+ actCfgPath string
+ actCfg *config.CGRConfig
+ actRPC *birpc.Client
+ actConfigDIR string //run tests for specific configuration
+
+ sTestsAct = []func(t *testing.T){
+ testActionsInitCfg,
+ testActionsInitDataDB,
+ testActionsResetStorDB,
+ testActionsStartEngine,
+ testActionsRPCConn,
+ testActionsGetActionProfileBeforeSet,
+ testActionsGetActionProfileIDsBeforeSet,
+ testActionsGetActionProfileCountBeforeSet,
+ testActionsSetActionProfile,
+ testActionsGetActionProfileAfterSet,
+ testActionsGetActionProfileIDsAfterSet,
+ testActionsGetActionProfileCountAfterSet,
+ testActionsRemoveActionProfile,
+ testActionsGetActionProfileAfterRemove,
+ testActionsPing,
+ // testActionsStartServer,
+ // testActionsStopServer,
+ testActionsKillEngine,
+ }
+)
+
+func TestActionsIT(t *testing.T) {
+ switch *dbType {
+ case utils.MetaInternal:
+ actConfigDIR = "apis_actions_internal"
+ case utils.MetaMongo:
+ actConfigDIR = "apis_actions_mongo"
+ case utils.MetaMySQL:
+ actConfigDIR = "apis_actions_mysql"
+ case utils.MetaPostgres:
+ t.SkipNow()
+ default:
+ t.Fatal("Unknown Database type")
+ }
+ for _, stest := range sTestsAct {
+ t.Run(actConfigDIR, stest)
+ }
+}
+
+func testActionsInitCfg(t *testing.T) {
+ var err error
+ actCfgPath = path.Join(*dataDir, "conf", "samples", actConfigDIR)
+ actCfg, err = config.NewCGRConfigFromPath(actCfgPath)
+ if err != nil {
+ t.Error(err)
+ }
+}
+
+func testActionsInitDataDB(t *testing.T) {
+ if err := engine.InitDataDB(actCfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func testActionsResetStorDB(t *testing.T) {
+ if err := engine.InitStorDB(actCfg); err != nil {
+ t.Fatal(err)
+ }
+}
+
+// Start CGR Engine
+func testActionsStartEngine(t *testing.T) {
+ if _, err := engine.StopStartEngine(actCfgPath, *waitRater); err != nil {
+ t.Fatal(err)
+ }
+}
+
+func testActionsRPCConn(t *testing.T) {
+ var err error
+ actRPC, err = newRPCClient(actCfg.ListenCfg()) // We connect over JSON so we can also troubleshoot if needed
+ if err != nil {
+ t.Fatal(err)
+ }
+}
+
+//Kill the engine when it is about to be finished
+func testActionsKillEngine(t *testing.T) {
+ if err := engine.KillEngine(100); err != nil {
+ t.Error(err)
+ }
+}
+
+func testActionsGetActionProfileBeforeSet(t *testing.T) {
+ var rplyAct engine.ActionProfile
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfile,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &rplyAct); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
+ }
+}
+
+func testActionsGetActionProfileIDsBeforeSet(t *testing.T) {
+ var rplyActIDs []string
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfileIDs,
+ &utils.PaginatorWithTenant{
+ Tenant: "cgrates.org",
+ Paginator: utils.Paginator{},
+ }, &rplyActIDs); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
+ }
+}
+
+func testActionsGetActionProfileCountBeforeSet(t *testing.T) {
+ var rplyCount int
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfileCount,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &rplyCount); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
+ } else if rplyCount != 0 {
+ t.Errorf("expected <%+v>, \nreceived: <%+v>", 0, rplyCount)
+ }
+}
+
+func testActionsSetActionProfile(t *testing.T) {
+ actPrf := &engine.ActionProfileWithAPIOpts{
+ ActionProfile: &engine.ActionProfile{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ Actions: []*engine.APAction{
+ {
+ ID: "actID",
+ },
+ },
+ },
+ }
+
+ var reply string
+ if err := actRPC.Call(context.Background(), utils.AdminSv1SetActionProfile,
+ actPrf, &reply); err != nil {
+ t.Error(err)
+ } else if reply != utils.OK {
+ t.Error("Unexpected reply returned:", reply)
+ }
+
+ var rplyActPrf engine.ActionProfile
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfile,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &rplyActPrf); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rplyActPrf, *actPrf.ActionProfile) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", actPrf.ActionProfile, rplyActPrf)
+ }
+}
+
+func testActionsGetActionProfileAfterSet(t *testing.T) {
+ expAct := engine.ActionProfile{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ Actions: []*engine.APAction{
+ {
+ ID: "actID",
+ },
+ },
+ }
+
+ var rplyAct engine.ActionProfile
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfile,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &rplyAct); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rplyAct, expAct) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>",
+ utils.ToJSON(expAct), utils.ToJSON(rplyAct))
+ }
+}
+
+func testActionsGetActionProfileIDsAfterSet(t *testing.T) {
+ expActIDs := []string{"actPrfID"}
+
+ var rplyActIDs []string
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfileIDs,
+ &utils.PaginatorWithTenant{
+ Tenant: "cgrates.org",
+ Paginator: utils.Paginator{},
+ }, &rplyActIDs); err != nil {
+ t.Error(err)
+ } else if !reflect.DeepEqual(rplyActIDs, expActIDs) {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", expActIDs, rplyActIDs)
+ }
+}
+
+func testActionsGetActionProfileCountAfterSet(t *testing.T) {
+ var rplyCount int
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfileCount,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &rplyCount); err != nil {
+ t.Error(err)
+ } else if rplyCount != 1 {
+ t.Errorf("expected <%+v>, \nreceived: <%+v>", 1, rplyCount)
+ }
+}
+
+func testActionsRemoveActionProfile(t *testing.T) {
+ var reply string
+ if err := actRPC.Call(context.Background(), utils.AdminSv1RemoveActionProfile,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &reply); err != nil {
+ t.Error(err)
+ } else if reply != utils.OK {
+ t.Error("Unexpected reply returned:", reply)
+ }
+}
+
+func testActionsGetActionProfileAfterRemove(t *testing.T) {
+ var rplyAct engine.ActionProfile
+ if err := actRPC.Call(context.Background(), utils.AdminSv1GetActionProfile,
+ &utils.TenantIDWithAPIOpts{
+ TenantID: &utils.TenantID{
+ Tenant: "cgrates.org",
+ ID: "actPrfID",
+ }}, &rplyAct); err == nil || err.Error() != utils.ErrNotFound.Error() {
+ t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err)
+ }
+}
+
+func testActionsPing(t *testing.T) {
+ var reply string
+ if err := actRPC.Call(context.Background(), utils.StatSv1Ping,
+ new(utils.CGREvent), &reply); err != nil {
+ t.Error(err)
+ } else if reply != utils.Pong {
+ t.Error("Unexpected reply returned:", reply)
+ }
+}
+
+// func testActionsStartServer(t *testing.T) {
+// actSrv = httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+// var err error
+// actBody, err = io.ReadAll(r.Body)
+// if err != nil {
+// w.WriteHeader(http.StatusNotFound)
+// }
+
+// r.Body.Close()
+// }))
+// }
+
+// func testActionsStopServer(t *testing.T) {
+// actSrv.Close()
+// }
diff --git a/apis/stats_it_test.go b/apis/stats_it_test.go
index 6e378eeff..a70a925a8 100644
--- a/apis/stats_it_test.go
+++ b/apis/stats_it_test.go
@@ -520,10 +520,12 @@ func testStatsSetActionProfileBeforeProcessEv(t *testing.T) {
},
}
- var reply *string
+ var reply string
if err := sqRPC.Call(context.Background(), utils.AdminSv1SetActionProfile,
actPrf, &reply); err != nil {
t.Error(err)
+ } else if reply != utils.OK {
+ t.Error("Unexpected reply returned:", reply)
}
var rplyActPrf engine.ActionProfile
diff --git a/data/conf/samples/apis_actions_internal/cgrates.json b/data/conf/samples/apis_actions_internal/cgrates.json
new file mode 100644
index 000000000..f0df98b30
--- /dev/null
+++ b/data/conf/samples/apis_actions_internal/cgrates.json
@@ -0,0 +1,37 @@
+{
+ // CGRateS Configuration file
+ // will be used in apis/actions_it_test.go
+
+ "general": {
+ "log_level": 7,
+ },
+
+ "data_db": {
+ "db_type": "*internal",
+ },
+
+ "stor_db": {
+ "db_type": "*internal",
+ },
+
+ "actions": {
+ "enabled": true,
+ "thresholds_conns": ["*internal"],
+ },
+
+ "stats": {
+ "enabled": true,
+ "store_interval": "-1",
+ "thresholds_conns": ["*internal"],
+ },
+
+ "thresholds": {
+ "enabled": true,
+ "store_interval": "-1",
+ "actions_conns": ["*internal"],
+ },
+
+ "admins": {
+ "enabled": true,
+ },
+ }
\ No newline at end of file
diff --git a/data/conf/samples/apis_actions_mongo/cgrates.json b/data/conf/samples/apis_actions_mongo/cgrates.json
new file mode 100644
index 000000000..4f95daa7b
--- /dev/null
+++ b/data/conf/samples/apis_actions_mongo/cgrates.json
@@ -0,0 +1,41 @@
+{
+ // CGRateS Configuration file
+ // will be used in apis/actions_it_test.go
+
+ "general": {
+ "log_level": 7,
+ },
+
+ "data_db": {
+ "db_type": "mongo",
+ "db_name": "10",
+ "db_port": 27017,
+ },
+
+ "stor_db": {
+ "db_type": "mongo",
+ "db_name": "cgrates",
+ "db_port": 27017,
+ },
+
+ "actions": {
+ "enabled": true,
+ "thresholds_conns": ["*internal"],
+ },
+
+ "stats": {
+ "enabled": true,
+ "store_interval": "-1",
+ "thresholds_conns": ["*internal"],
+ },
+
+ "thresholds": {
+ "enabled": true,
+ "store_interval": "-1",
+ "actions_conns": ["*internal"],
+ },
+
+ "admins": {
+ "enabled": true,
+ }
+}
\ No newline at end of file
diff --git a/data/conf/samples/apis_actions_mysql/cgrates.json b/data/conf/samples/apis_actions_mysql/cgrates.json
new file mode 100644
index 000000000..ebde660f6
--- /dev/null
+++ b/data/conf/samples/apis_actions_mysql/cgrates.json
@@ -0,0 +1,38 @@
+{
+ // CGRateS Configuration file
+ // will be used in apis/actions_it_test.go
+ "general": {
+ "log_level": 7,
+ },
+
+ "data_db": { // database used to store runtime data (eg: accounts, cdr stats)
+ "db_type": "redis", // data_db type:
+ "db_port": 6379, // data_db port to reach the database
+ "db_name": "10", // data_db database name to connect to
+ },
+
+ "stor_db": {
+ "db_password": "CGRateS.org",
+ },
+
+ "actions": {
+ "enabled": true,
+ "thresholds_conns": ["*internal"],
+ },
+
+ "stats": {
+ "enabled": true,
+ "store_interval": "-1",
+ "thresholds_conns": ["*internal"],
+ },
+
+ "thresholds": {
+ "enabled": true,
+ "store_interval": "-1",
+ "actions_conns": ["*internal"],
+ },
+
+ "admins": {
+ "enabled": true,
+ }
+}
\ No newline at end of file