//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 general_tests import ( "bytes" "testing" "time" "github.com/cgrates/birpc/context" v1 "github.com/cgrates/cgrates/apier/v1" "github.com/cgrates/cgrates/engine" "github.com/cgrates/cgrates/sessions" "github.com/cgrates/cgrates/utils" ) func TestSessionInitiateDisableAccount(t *testing.T) { cfgJson := `{ "general": { "log_level": 7, "reply_timeout": "50s", }, "listen": { "rpc_json": ":2012", "rpc_gob": ":2013", "http": ":2080", }, "data_db": { "db_type": "redis", "db_port": 6379, "db_name": "10", }, "stor_db": { "db_password": "CGRateS.org", }, "rals": { "enabled": true, "thresholds_conns": ["*internal"], "max_increments":3000000, }, "cdrs": { "enabled": true, "chargers_conns":["*internal"], "attributes_conns":["*localhost"], "rals_conns":["*localhost"], "stats_conns":["*localhost"] }, "attributes": { "enabled": true, "stats_conns": ["*localhost"], "resources_conns": ["*localhost"], "apiers_conns": ["*localhost"] }, "chargers": { "enabled": true, "attributes_conns": ["*internal"], }, "stats": { "enabled": true, "store_interval": "1s", "thresholds_conns": ["*localhost"], }, "thresholds": { "enabled": true, "store_interval": "1s", "sessions_conns": ["*localhost"], "apiers_conns": ["*internal"] }, "sessions": { "enabled": true, "attributes_conns": ["*internal"], "rals_conns": ["*internal"], "cdrs_conns": ["*localhost"], "chargers_conns": ["*internal"], }, "apiers": { "enabled": true, "scheduler_conns": ["*internal"], }, "schedulers": { "enabled": true, "cdrs_conns": ["*internal"], "stats_conns": ["*localhost"], }, } ` var dbCfg engine.DBCfg switch *utils.DBType { case utils.MetaInternal: dbCfg = engine.DBCfg{StorDB: &engine.DBParams{Type: utils.StringPointer("*internal")}} case utils.MetaMySQL: dbCfg = engine.DBCfg{StorDB: &engine.DBParams{ Type: utils.StringPointer("*mysql"), Password: utils.StringPointer("CGRateS.org"), }} case utils.MetaMongo: dbCfg = engine.DBCfg{StorDB: &engine.DBParams{ Type: utils.StringPointer("*mongo"), Name: utils.StringPointer("cgrates"), Port: utils.IntPointer(27017), }} case utils.MetaPostgres: t.SkipNow() default: t.Fatalf("unsupported dbtype %v", *utils.DBType) } ng := engine.TestEngine{ ConfigJSON: cfgJson, DBCfg: dbCfg, TpFiles: map[string]string{ utils.AccountActionsCsv: `#Tenant,Account,ActionPlanId,ActionTriggersId,AllowNegative,Disabled cgrates.org,1001,AP1,DISABLE_TRIGGER,,`, utils.ActionPlansCsv: `#Id,ActionsId,TimingId,Weight AP1,ACT_MON,*asap,10`, utils.ActionsCsv: `#ActionsId,Action,ExtraParameters,Filter,BalanceId,BalanceType,Categories,DestinationIds,RatingSubject,SharedGroup,ExpiryTime,TimingIds,Units,BalanceWeight,BalanceBlocker,BalanceDisabled,Weight ACT_MON,*topup_reset,,,balance1,*monetary,,,,,,,10,,false,, DISABLE_ACC,*disable_account,,,,,,,,,,,,,,,`, utils.ActionTriggersCsv: `#Tag[0],UniqueId[1],ThresholdType[2],ThresholdValue[3],Recurrent[4],MinSleep[5],ExpiryTime[6],ActivationTime[7],BalanceTag[8],BalanceType[9],BalanceCategories[10],BalanceDestinationIds[11],BalanceRatingSubject[12],BalanceSharedGroup[13],BalanceExpiryTime[14],BalanceTimingIds[15],BalanceWeight[16],BalanceBlocker[17],BalanceDisabled[18],ActionsId[19],Weight[20] DISABLE_TRIGGER,,*max_event_connect,1,false,0,,,,*event_connect,,,,,,,,,,DISABLE_ACC,10`, utils.ChargersCsv: `#Tenant,ID,FilterIDs,ActivationInterval,RunID,AttributeIDs,Weight cgrates.org,DEFAULT,,,*default,*none,0`, utils.RatingProfilesCsv: `#Tenant,Category,Subject,ActivationTime,RatingPlanId,RatesFallbackSubject cgrates.org,call,1001,2014-01-14T00:00:00Z,RP,`, utils.RatingPlansCsv: `#Id,DestinationRatesId,TimingTag,Weight RP,DR_RP,*any,10`, utils.DestinationRatesCsv: `#Id,DestinationId,RatesTag,RoundingMethod,RoundingDecimals,MaxCost,MaxCostStrategy DR_RP,DST_1002,RT1,*up,4,0,`, utils.DestinationsCsv: `#Id,Prefix DST_1002,1002`, utils.RatesCsv: `#Id,ConnectFee,Rate,RateUnit,RateIncrement,GroupIntervalStart RT1,0.2,0.1,1s,1s,0`, utils.AttributesCsv: `#Tenant,ID,Context,FilterIDs,ActivationInterval,AttributeFilterIDs,Path,Type,Value,Blocker,Weight cgrates.org,ATTR_ACNT,*any,,,,*opts.*accountID,*variable,~*req.Account,false,10`, utils.StatsCsv: `#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],QueueLength[4],TTL[5],MinItems[6],Metrics[7],MetricFilterIDs[8],Stored[9],Blocker[10],Weight[11],ThresholdIDs[12] cgrates.org,STATS_1001,*string:~*req.Account:1001,,,-1,,*sum#1,,true,,,THD_1001`, utils.ThresholdsCsv: `#Tenant[0],Id[1],FilterIDs[2],ActivationInterval[3],MaxHits[4],MinHits[5],MinSleep[6],Blocker[7],Weight[8],ActionIDs[9],Async[10],EeIDs[11] cgrates.org,THD_1001,*string:~*req.StatID:STATS_1001,,-1,5,0,false,,DISABLE_ACC,false,`, }, LogBuffer: bytes.NewBuffer(nil), } //defer t.Log(ng.LogBuffer) client, _ := ng.Run(t) t.Run("AuthorizeEvent", func(t *testing.T) { var accRepl engine.Account if err := client.Call(context.Background(), utils.APIerSv2GetAccount, &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}, &accRepl); err != nil { t.Error(err) } if accRepl.Disabled { t.Errorf("account should not be disabled") } var reply sessions.V1AuthorizeReply if err := client.Call(context.Background(), utils.SessionSv1AuthorizeEvent, &sessions.V1AuthorizeArgs{GetAttributes: true, GetMaxUsage: true, CGREvent: &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.GenUUID(), Time: utils.TimePointer(time.Now()), Event: map[string]any{"Account": "1001", "Destination": "1002", "OriginHost": "127.0.0.1:8448", "RequestType": "*prepaid", "SetupTime": "1747212851", "Source": "KamailioAgent"}}}, &reply); err != nil { t.Error(err) } if err := client.Call(context.Background(), utils.APIerSv2GetAccount, &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}, &accRepl); err != nil { t.Error(err) } else if *reply.MaxUsage != (98 * time.Second) { t.Errorf("expected to get usage even though account got disabled") } if accRepl.Disabled { t.Errorf("expected account to be not be disabled") } }) t.Run("DisableAccountFromThresholds", func(t *testing.T) { var replyStr string rmReq := v1.AttrRemoveAccountActionTriggers{Tenant: "cgrates.org", Account: "1001", GroupID: "DISABLE_TRIGGER"} if err := client.Call(context.Background(), utils.APIerSv1RemoveAccountActionTriggers, rmReq, &replyStr); err != nil { t.Error("Got error on APIerSv1.RemoveActionTiming: ", err.Error()) } else if replyStr != utils.OK { t.Error("Unexpected answer received", replyStr) } for range 5 { argsEv := &engine.ArgV1ProcessEvent{ Flags: []string{utils.MetaRALs, utils.MetaAttributes}, CGREvent: utils.CGREvent{ Tenant: "cgrates.org", ID: utils.GenUUID(), Event: map[string]any{ utils.RunID: "run_1", utils.CGRID: utils.GenUUID(), utils.Tenant: "cgrates.org", utils.Category: "call", utils.OriginID: "origin1", utils.OriginHost: "OriginHost1", utils.RequestType: utils.MetaPrepaid, utils.Subject: "1001", utils.AccountField: "1001", utils.Destination: "1002", utils.SetupTime: time.Date(2021, time.February, 2, 16, 14, 50, 0, time.UTC), utils.AnswerTime: time.Date(2021, time.February, 2, 16, 15, 0, 0, time.UTC), utils.Usage: 10 * time.Second, }, }, } if err := client.Call(context.Background(), utils.CDRsV1ProcessEvent, argsEv, &replyStr); err != nil { t.Error("Unexpected error: ", err.Error()) } else if replyStr != utils.OK { t.Error("Unexpected reply received: ", replyStr) } } var accRepl engine.Account if err := client.Call(context.Background(), utils.APIerSv2GetAccount, &utils.AttrGetAccount{Tenant: "cgrates.org", Account: "1001"}, &accRepl); err != nil { t.Error(err) } if !accRepl.Disabled { t.Errorf("expected account to be disabled") } t.Run("AuthorizeEventWithAccountDisabled", func(t *testing.T) { var reply string if err := client.Call(context.Background(), utils.SessionSv1AuthorizeEvent, &sessions.V1AuthorizeArgs{GetAttributes: true, GetMaxUsage: true, CGREvent: &utils.CGREvent{ Tenant: "cgrates.org", ID: utils.GenUUID(), Time: utils.TimePointer(time.Now()), Event: map[string]any{"Account": "1001", "Destination": "1002", "OriginHost": "127.0.0.1:8448", "RequestType": "*prepaid", "SetupTime": "1747212851", "Source": "KamailioAgent"}}}, &reply); err == nil || err.Error() != utils.NewErrRALs(utils.ErrAccountDisabled).Error() { t.Errorf("expected: ACCOUNT_DISABLED error, got: %v", err) } }) }) }