From 180eb6cbfdf46df0f952a654970a380afe44ba8e Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Wed, 28 May 2025 16:25:02 +0300 Subject: [PATCH] extract resources apis to different file --- data/conf/samples/ees_ooma/cgrates.json | 628 ----- resources/apis.go | 381 +++ resources/apis_test.go | 2809 +++++++++++++++++++++++ resources/resources.go | 352 --- resources/resources_test.go | 2777 +--------------------- 5 files changed, 3191 insertions(+), 3756 deletions(-) delete mode 100644 data/conf/samples/ees_ooma/cgrates.json create mode 100644 resources/apis.go create mode 100644 resources/apis_test.go diff --git a/data/conf/samples/ees_ooma/cgrates.json b/data/conf/samples/ees_ooma/cgrates.json deleted file mode 100644 index 1b759693e..000000000 --- a/data/conf/samples/ees_ooma/cgrates.json +++ /dev/null @@ -1,628 +0,0 @@ -{ -// Sample CGRateS Configuration file for EEs -// -// Copyright (C) ITsysCOM GmbH - -"logger": { - "level": 7 -}, - -"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", -}, - - -"rates": { - "enabled": true, -}, - - -"cdrs": { - "enabled": true, - "chargers_conns": ["*localhost"], - "rates_conns": ["*internal"], - "session_cost_retries": 0, -}, - - -"chargers": { - "enabled": true, - "attributes_conns": ["*internal"], -}, - - -"attributes": { - "enabled": true, -}, - - -"ees": { - "attributes_conns": [], - "cache": { - "*file_csv": { - "limit": -1, - "static_ttl": false, - "ttl": "5s" - }, - "*sql": { - "limit": -1, - "static_ttl": false, - "ttl": "1h" - } - }, - "enabled": true, - "exporters": [ - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-02" - ], - "flags": [], - "id": "monthly_cdrs_1", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-03" - ], - "flags": [], - "id": "monthly_cdrs_2", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-04" - ], - "flags": [], - "id": "monthly_cdrs_3", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-05" - ], - "flags": [], - "id": "monthly_cdrs_4", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-06" - ], - "flags": [], - "id": "monthly_cdrs_5", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-07" - ], - "flags": [], - "id": "monthly_cdrs_6", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-08" - ], - "flags": [], - "id": "monthly_cdrs_7", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-09" - ], - "flags": [], - "id": "monthly_cdrs_8", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-10" - ], - "flags": [], - "id": "monthly_cdrs_9", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-11" - ], - "flags": [], - "id": "monthly_cdrs_10", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2022-12" - ], - "flags": [], - "id": "monthly_cdrs_11", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2023-01" - ], - "flags": [], - "id": "monthly_cdrs_12", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - }, - { - "attempts": 2, - "attribute_context": "", - "attribute_ids": [], - "export_path": "mysql://cgrates:CGRateS.org@127.0.0.1:3306", - "fields": [ - { - "tag": "RequiredTemplate", - "type": "*template", - "value": "requiredFields" - } - ], - "filters": [ - "*prefix:~*req.SetupTime:2023-02" - ], - "flags": [], - "id": "monthly_cdrs_13", - "opts": { - "sqlConnMaxLifetime": "0", - "sqlDBName": "cgrates2", - "sqlMaxIdleConns": 10, - "sqlMaxOpenConns": 100, - "sqlTableName": "cdrs", - "pgSSLMode": "disable" - }, - "tenant": "", - "timezone": "", - "type": "*sql" - } - ] -}, - - -"apiers": { - "enabled": true, -}, - - -"templates": { - "requiredFields": [ - { - "path": "*exp.cgrid", - "tag": "cgrid", - "type": "*variable", - "value": "~*req.CGRID" - }, - { - "path": "*exp.run_id", - "tag": "run_id", - "type": "*variable", - "value": "~*req.RunID" - }, - { - "path": "*exp.origin_host", - "tag": "origin_host", - "type": "*variable", - "value": "~*req.OriginHost" - }, - { - "path": "*exp.source", - "tag": "source", - "type": "*variable", - "value": "~*req.Source" - }, - { - "path": "*exp.origin_id", - "tag": "origin_id", - "type": "*variable", - "value": "~*req.OriginID" - }, - { - "path": "*exp.tor", - "tag": "tor", - "type": "*variable", - "value": "~*req.ToR" - }, - { - "path": "*exp.request_type", - "tag": "request_type", - "type": "*variable", - "value": "~*req.RequestType" - }, - { - "path": "*exp.tenant", - "tag": "tenant", - "type": "*variable", - "value": "~*req.Tenant" - }, - { - "path": "*exp.category", - "tag": "category", - "type": "*variable", - "value": "~*req.Category" - }, - { - "path": "*exp.account", - "tag": "account", - "type": "*variable", - "value": "~*req.Account" - }, - { - "path": "*exp.subject", - "tag": "subject", - "type": "*variable", - "value": "~*req.Subject" - }, - { - "path": "*exp.destination", - "tag": "destination", - "type": "*variable", - "value": "~*req.Destination" - }, - { - "path": "*exp.setup_time", - "tag": "setup_time", - "type": "*variable", - "value": "~*req.SetupTime" - }, - { - "path": "*exp.answer_time", - "tag": "answer_time", - "type": "*variable", - "value": "~*req.AnswerTime" - }, - { - "path": "*exp.`usage`", - "tag": "usage", - "type": "*variable", - "value": "~*req.Usage{*duration_nanoseconds}" - }, - { - "path": "*exp.cost_source", - "tag": "cost_source", - "type": "*variable", - "value": "~*req.CostSource" - }, - { - "path": "*exp.cost", - "tag": "cost", - "type": "*variable", - "value": "~*req.Cost" - }, - { - "path": "*exp.cost_details", - "tag": "cost_details", - "type": "*variable", - "value": "~*req.CostDetails" - }, - { - "path": "*exp.extra_info", - "tag": "extra_info", - "type": "*variable", - "value": "~*req.ExtraInfo" - }, - { - "path": "*exp.disconnect_cause", - "tag": "disconnect_cause", - "type": "*variable", - "value": "~*req.DisconnectCause" - }, - { - "path": "*exp.route", - "tag": "route", - "type": "*variable", - "value": "~*req.Route" - }, - { - "path": "*exp.caller_is_determined_in_areas", - "tag": "caller_is_determined_in_areas", - "type": "*variable", - "value": "~*req.caller_is_determined_in_areas" - }, - { - "path": "*exp.caller_is_determined", - "tag": "caller_is_determined", - "type": "*variable", - "value": "~*req.variable_caller_is_determined" - }, - { - "path": "*exp.origination", - "tag": "origination", - "type": "*variable", - "value": "~*req.Account" - }, - { - "path": "*exp.e164_destination", - "tag": "e164_destination", - "type": "*variable", - "value": "~*req.variable_e164_destination" - }, - { - "path": "*exp.rn_destination", - "tag": "rn_destination", - "type": "*variable", - "value": "~*req.variable_rn_destination" - }, - { - "path": "*exp.original_destination", - "tag": "original_destination", - "type": "*variable", - "value": "~*req.variable_original_destination" - }, - { - "path": "*exp.original_account", - "tag": "original_account", - "type": "*variable", - "value": "~*req.variable_original_account" - } - ] -}, - - -} diff --git a/resources/apis.go b/resources/apis.go new file mode 100644 index 000000000..6b1186314 --- /dev/null +++ b/resources/apis.go @@ -0,0 +1,381 @@ +/* +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 resources + +import ( + "time" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" + "github.com/cgrates/guardian" +) + +// V1GetResourcesForEvent returns active resource configs matching the event +func (rS *ResourceS) V1GetResourcesForEvent(ctx *context.Context, args *utils.CGREvent, reply *Resources) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, + utils.OptsResourcesUsageID); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, + utils.OptsResourcesUsageTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1GetResourcesForEvent, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*Resources) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs Resources + if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, usageTTL); err != nil { + return err + } + *reply = mtcRLs + mtcRLs.unlock() + return +} + +// V1AuthorizeResources queries service to find if an Usage is allowed +func (rS *ResourceS) V1AuthorizeResources(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, + utils.OptsResourcesUsageID); err != nil { + return + } + + var units float64 + if units, err = engine.GetFloat64Opts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.Units, + utils.OptsResourcesUnits); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, + utils.OptsResourcesUsageTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AuthorizeResources, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs Resources + if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, usageTTL); err != nil { + return err + } + defer mtcRLs.unlock() + + var alcMessage string + if alcMessage, err = mtcRLs.allocateResource(&utils.ResourceUsage{ + Tenant: tnt, + ID: usageID, + Units: units}, true); err != nil { + if err == utils.ErrResourceUnavailable { + err = utils.ErrResourceUnauthorized + } + return + } + *reply = alcMessage + return +} + +// V1AllocateResources is called when a resource requires allocation +func (rS *ResourceS) V1AllocateResources(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, + utils.OptsResourcesUsageID); err != nil { + return + } + + var units float64 + if units, err = engine.GetFloat64Opts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.Units, + utils.OptsResourcesUnits); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, + utils.OptsResourcesUsageTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AllocateResources, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs Resources + if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, + usageTTL); err != nil { + return err + } + defer mtcRLs.unlock() + + var alcMsg string + if alcMsg, err = mtcRLs.allocateResource(&utils.ResourceUsage{Tenant: tnt, ID: usageID, + Units: units}, false); err != nil { + return + } + + // index it for storing + if err = rS.storeMatchedResources(ctx, mtcRLs); err != nil { + return + } + if err = rS.processThresholds(ctx, mtcRLs, args.APIOpts); err != nil { + return + } + *reply = alcMsg + return +} + +// V1ReleaseResources is called when we need to clear an allocation +func (rS *ResourceS) V1ReleaseResources(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { + if args == nil { + return utils.NewErrMandatoryIeMissing(utils.Event) + } + if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + + var usageID string + if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, + utils.OptsResourcesUsageID); err != nil { + return + } + + var ttl time.Duration + if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, + utils.OptsResourcesUsageTTL); err != nil { + return + } + usageTTL := utils.DurationPointer(ttl) + + if usageID == utils.EmptyString { + return utils.NewErrMandatoryIeMissing(utils.UsageID) + } + + tnt := args.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + + // RPC caching + if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1ReleaseResources, utils.ConcatenatedKey(tnt, args.ID)) + refID := guardian.Guardian.GuardIDs("", + config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic + defer guardian.Guardian.UnguardIDs(refID) + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + cachedResp := itm.(*utils.CachedRPCResponse) + if cachedResp.Error == nil { + *reply = *cachedResp.Result.(*string) + } + return cachedResp.Error + } + defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: reply, Error: err}, + nil, true, utils.NonTransactional) + } + // end of RPC caching + + var mtcRLs Resources + if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, + usageTTL); err != nil { + return + } + defer mtcRLs.unlock() + + if err = mtcRLs.clearUsage(usageID); err != nil { + return + } + + // Handle storing + if err = rS.storeMatchedResources(ctx, mtcRLs); err != nil { + return + } + if err = rS.processThresholds(ctx, mtcRLs, args.APIOpts); err != nil { + return + } + + *reply = utils.OK + return +} + +// V1GetResource returns a resource configuration +func (rS *ResourceS) V1GetResource(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.Resource) error { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + + // make sure resource is locked at process level + lkID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.ResourceLockKey(tnt, arg.ID)) + defer guardian.Guardian.UnguardIDs(lkID) + + res, err := rS.dm.GetResource(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return err + } + *reply = *res + return nil +} + +func (rS *ResourceS) V1GetResourceWithConfig(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.ResourceWithConfig) (err error) { + if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing + return utils.NewErrMandatoryIeMissing(missing...) + } + tnt := arg.Tenant + if tnt == utils.EmptyString { + tnt = rS.cfg.GeneralCfg().DefaultTenant + } + + // make sure resource is locked at process level + lkID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.ResourceLockKey(tnt, arg.ID)) + defer guardian.Guardian.UnguardIDs(lkID) + + var res *utils.Resource + res, err = rS.dm.GetResource(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return + } + + // make sure resourceProfile is locked at process level + lkPrflID := guardian.Guardian.GuardIDs(utils.EmptyString, + config.CgrConfig().GeneralCfg().LockingTimeout, + utils.ResourceProfileLockKey(tnt, arg.ID)) + defer guardian.Guardian.UnguardIDs(lkPrflID) + + var cfg *utils.ResourceProfile + cfg, err = rS.dm.GetResourceProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional) + if err != nil { + return + } + + *reply = utils.ResourceWithConfig{ + Resource: res, + Config: cfg, + } + + return +} diff --git a/resources/apis_test.go b/resources/apis_test.go new file mode 100644 index 000000000..d90134434 --- /dev/null +++ b/resources/apis_test.go @@ -0,0 +1,2809 @@ +/* +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 resources + +import ( + "reflect" + "testing" + "time" + + "github.com/cgrates/birpc" + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/rpcclient" + + "github.com/cgrates/cgrates/utils" +) + +func TestResourceV1AuthorizeResourceMissingStruct(t *testing.T) { + var dmRES *engine.DataManager + cfg := config.NewDefaultCGRConfig() + + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dmRES = engine.NewDataManager(data, cfg, nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + fltrs := engine.NewFilterS(cfg, nil, dmRES) + resService := NewResourceService(dmRES, cfg, + fltrs, nil) + var reply *string + argsMissingTenant := &utils.CGREvent{ + ID: "id1", + Event: map[string]any{}, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "test1", + utils.OptsResourcesUnits: 20, + }, + } + argsMissingUsageID := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "id1", + Event: map[string]any{}, + APIOpts: map[string]any{ + utils.OptsResourcesUnits: 20, + }, + } + if err := resService.V1AuthorizeResources(context.TODO(), argsMissingTenant, reply); err != nil && err.Error() != "MANDATORY_IE_MISSING: [Event]" { + t.Error(err.Error()) + } + if err := resService.V1AuthorizeResources(context.TODO(), argsMissingUsageID, reply); err != nil && err.Error() != "MANDATORY_IE_MISSING: [Event]" { + t.Error(err.Error()) + } +} + +func TestResourceAllocateResourceOtherDB(t *testing.T) { + rProf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RL_DB", + FilterIDs: []string{"*string:~*opts.Resource:RL_DB"}, + Weights: utils.DynamicWeights{ + { + Weight: 100, + }}, + Limit: 2, + ThresholdIDs: []string{utils.MetaNone}, + UsageTTL: -time.Nanosecond, + } + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + idb, err := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + if err != nil { + t.Error(err) + } + dm := engine.NewDataManager(idb, cfg, nil) + fltS := engine.NewFilterS(cfg, nil, dm) + rs := NewResourceService(dm, cfg, fltS, nil) + if err := dm.SetResourceProfile(context.TODO(), rProf, true); err != nil { + t.Fatal(err) + } + if err := dm.SetResource(context.TODO(), &utils.Resource{ + Tenant: "cgrates.org", + ID: "RL_DB", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { // the resource in DB is expired (should be cleaned when the next allocate is called) + Tenant: "cgrates.org", + ID: "RU1", + ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), + Units: 1, + }, + }, + TTLIdx: []string{"RU1"}, + }); err != nil { // simulate how the resource is stored in redis or mongo(non-exported fields are not populated) + t.Fatal(err) + } + var reply string + exp := rProf.ID + if err := rs.V1AllocateResources(context.TODO(), &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "ef0f554", + Event: map[string]any{"": ""}, + APIOpts: map[string]any{ + "Resource": "RL_DB", + utils.OptsResourcesUsageID: "56156434-2e44-4f16-a766-086f10b413cd", + utils.OptsResourcesUnits: 1, + }, + }, &reply); err != nil { + t.Fatal(err) + } else if reply != exp { + t.Errorf("Expected: %q, received: %q", exp, reply) + } + +} + +func TestResourcesV1ResourcesForEventOK(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + TTLIdx: []string{}, + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_TEST1", + }, + } + + exp := Resources{ + { + Resource: &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + TTLIdx: []string{}, + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + }, + rPrf: &resourceProfile{ResourceProfile: rsPrf}, + ttl: utils.DurationPointer(72 * time.Hour), + }, + } + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, exp) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + utils.ToJSON(exp), utils.ToJSON(reply)) + } +} + +func TestResourcesV1ResourcesForEventNotFound(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1002", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_TEST1", + }, + } + + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1ResourcesForEventMissingParameters(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + experr := `MANDATORY_IE_MISSING: [Event]` + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), nil, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args := &utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_TEST2", + }, + } + + experr = `MANDATORY_IE_MISSING: [ID]` + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args = &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "ResourcesForEventTest", + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_TEST3", + }, + } + + experr = `MANDATORY_IE_MISSING: [Event]` + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args = &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + } + + experr = `MANDATORY_IE_MISSING: [UsageID]` + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1ResourcesForEventCacheReplyExists(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1GetResourcesForEvent, + utils.ConcatenatedKey("cgrates.org", "ResourcesForEventTest")) + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_TEST1", + }, + } + + cacheReply := Resources{ + { + Resource: &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + }, + rPrf: rsPrf, + dirty: utils.BoolPointer(false), + tUsage: utils.Float64Pointer(10), + ttl: utils.DurationPointer(time.Minute), + }, + } + engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, + nil, true, utils.NonTransactional) + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, cacheReply) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + utils.ToJSON(cacheReply), utils.ToJSON(reply)) + } + + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1ResourcesForEventCacheReplySet(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1GetResourcesForEvent, + utils.ConcatenatedKey("cgrates.org", "ResourcesForEventTest")) + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_TEST1", + }, + } + + exp := &Resources{ + { + Resource: &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + }, + ttl: utils.DurationPointer(72 * time.Hour), + rPrf: &resourceProfile{ + ResourceProfile: rsPrf, + }, + }, + } + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, *exp) { + t.Errorf("expected: <%v>, received: <%v>", exp, reply) + } + + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + resp := itm.(*utils.CachedRPCResponse) + if !reflect.DeepEqual(resp.Result, exp) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(exp), utils.ToJSON(resp.Result)) + } + } + + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1GetResourceOK(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + exp := utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + TTLIdx: []string{}, + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + } + + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "RES1", + }, + } + var reply utils.Resource + if err := rS.V1GetResource(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, exp) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + utils.ToJSON(exp), utils.ToJSON(reply)) + } +} + +func TestResourcesV1GetResourceNotFound(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + Tenant: "cgrates.org", + ID: "RES2", + }, + } + var reply utils.Resource + if err := rS.V1GetResource(context.Background(), args, &reply); err == nil || + err.Error() != utils.ErrNotFound.Error() { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1GetResourceMissingParameters(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{}, + } + + experr := `MANDATORY_IE_MISSING: [ID]` + var reply utils.Resource + if err := rS.V1GetResource(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1GetResourceWithConfigOK(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + exp := utils.ResourceWithConfig{ + Resource: &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + }, + Config: rsPrf, + } + + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "RES1", + }, + } + var reply utils.ResourceWithConfig + if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if !reflect.DeepEqual(reply, exp) { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + utils.ToJSON(exp), utils.ToJSON(reply)) + } +} + +func TestResourcesV1GetResourceWithConfigNilrPrfProfileNotFound(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES2", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "RES1", + }, + } + var reply utils.ResourceWithConfig + if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err == nil || + err != utils.ErrNotFound { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1GetResourceWithConfigResourceNotFound(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES2", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{ + ID: "RES1", + }, + } + var reply utils.ResourceWithConfig + if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err == nil || err != utils.ErrNotFound { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1GetResourceWithConfigMissingParameters(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + err := dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + experr := `MANDATORY_IE_MISSING: [ID]` + args := &utils.TenantIDWithAPIOpts{ + TenantID: &utils.TenantID{}, + } + var reply utils.ResourceWithConfig + if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err == nil || err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AuthorizeResourcesOK(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } +} + +func TestResourcesV1AuthorizeResourcesNotAuthorized(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 0, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || + err != utils.ErrResourceUnauthorized { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrResourceUnauthorized, err) + } +} + +func TestResourcesV1AuthorizeResourcesNoMatch(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1002", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || + err != utils.ErrNotFound { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1AuthorizeResourcesNilCGREvent(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + experr := `MANDATORY_IE_MISSING: [Event]` + var reply string + + if err := rS.V1AuthorizeResources(context.Background(), nil, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AuthorizeResourcesMissingUsageID(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + experr := `MANDATORY_IE_MISSING: [UsageID]` + var reply string + + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AuthorizeResourcesCacheReplyExists(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AuthorizeResources, + utils.ConcatenatedKey("cgrates.org", "EventAuthorizeResource")) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + cacheReply := "Approved" + engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, + nil, true, utils.NonTransactional) + + var reply string + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != cacheReply { + t.Errorf("Unexpected reply returned: %q", reply) + } + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1AuthorizeResourcesCacheReplySet(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AuthorizeResources, + utils.ConcatenatedKey("cgrates.org", "EventAuthorizeResource")) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 4, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 2, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + var reply string + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } + + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + resp := itm.(*utils.CachedRPCResponse) + if *resp.Result.(*string) != "Approved" { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + "Approved", *resp.Result.(*string)) + } + } + + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1AllocateResourcesOK(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } +} + +func TestResourcesV1AllocateResourcesNoMatch(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + }, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1002", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err != utils.ErrNotFound { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1AllocateResourcesMissingParameters(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + experr := `MANDATORY_IE_MISSING: [UsageID]` + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args = &utils.CGREvent{ + ID: "EventAuthorizeResource", + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + experr = `MANDATORY_IE_MISSING: [Event]` + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args = &utils.CGREvent{ + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + experr = `MANDATORY_IE_MISSING: [ID]` + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + experr = `MANDATORY_IE_MISSING: [Event]` + if err := rS.V1AllocateResources(context.Background(), nil, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AllocateResourcesCacheReplyExists(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AllocateResources, + utils.ConcatenatedKey("cgrates.org", "EventAllocateResource")) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAllocateResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + cacheReply := "cacheApproved" + engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, + nil, true, utils.NonTransactional) + + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != cacheReply { + t.Errorf("Unexpected reply returned: %q", reply) + } + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1AllocateResourcesCacheReplySet(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AllocateResources, + utils.ConcatenatedKey("cgrates.org", "EventAllocateResource")) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 4, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAllocateResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 2, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } + + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + resp := itm.(*utils.CachedRPCResponse) + if *resp.Result.(*string) != "Approved" { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + "Approved", *resp.Result.(*string)) + } + } + + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1AllocateResourcesResAllocErr(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err != utils.ErrResourceUnavailable { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrResourceUnavailable, err) + } +} + +type ccMock struct { + calls map[string]func(ctx *context.Context, args any, reply any) error +} + +func (ccM *ccMock) Call(ctx *context.Context, serviceMethod string, args any, reply any) (err error) { + if call, has := ccM.calls[serviceMethod]; !has { + return rpcclient.ErrUnsupporteServiceMethod + } else { + return call(ctx, args, reply) + } +} + +func TestResourcesV1AllocateResourcesProcessThErr(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().StoreInterval = 2 + cfg.ResourceSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)} + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + ccM := &ccMock{ + calls: map[string]func(ctx *context.Context, args any, reply any) error{ + utils.ThresholdSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { + return utils.ErrExists + }, + }, + } + rpcInternal := make(chan birpc.ClientConnector, 1) + rpcInternal <- ccM + cM := engine.NewConnManager(cfg) + cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, cM) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err != utils.ErrPartiallyExecuted { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrPartiallyExecuted, err) + } + dm.DataDB().Flush(utils.EmptyString) +} + +func TestResourcesV1ReleaseResourcesOK(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } + + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != utils.OK { + t.Errorf("Unexpected reply returned: %q", reply) + } +} + +func TestResourcesV1ReleaseResourcesUsageNotFound(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: 0, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } + + args = &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test2", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + experr := `cannot find usage record with id: RU_Test2` + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1ReleaseResourcesNoMatch(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1002", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err != utils.ErrNotFound { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) + } +} + +func TestResourcesV1ReleaseResourcesMissingParameters(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + + experr := `MANDATORY_IE_MISSING: [UsageID]` + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args = &utils.CGREvent{ + ID: "EventAuthorizeResource", + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + experr = `MANDATORY_IE_MISSING: [Event]` + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + args = &utils.CGREvent{ + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + experr = `MANDATORY_IE_MISSING: [ID]` + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + experr = `MANDATORY_IE_MISSING: [Event]` + if err := rS.V1ReleaseResources(context.Background(), nil, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1ReleaseResourcesCacheReplyExists(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1ReleaseResources, + utils.ConcatenatedKey("cgrates.org", "EventReleaseResource")) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 10, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + Tenant: "cgrates.org", + ID: "EventReleaseResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + cacheReply := "cacheReply" + engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, + &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, + nil, true, utils.NonTransactional) + + var reply string + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != cacheReply { + t.Errorf("Unexpected reply returned: %q", reply) + } + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1ReleaseResourcesCacheReplySet(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 + config.SetCgrConfig(cfg) + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + cacheKey := utils.ConcatenatedKey(utils.ResourceSv1ReleaseResources, + utils.ConcatenatedKey("cgrates.org", "EventReleaseResource")) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + } + rs := &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU1": { + Tenant: "cgrates.org", + ID: "RU1", + Units: 4, + }, + }, + TTLIdx: []string{}, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "EventReleaseResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 2, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + + var reply string + experr := `cannot find usage record with id: RU_Test` + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } + + if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { + resp := itm.(*utils.CachedRPCResponse) + if *resp.Result.(*string) != "" { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", + "", *resp.Result.(*string)) + } + } + + config.SetCgrConfig(config.NewDefaultCGRConfig()) +} + +func TestResourcesV1ReleaseResourcesProcessThErr(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().StoreInterval = 2 + cfg.ResourceSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)} + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + ccM := &ccMock{ + calls: map[string]func(ctx *context.Context, args any, reply any) error{ + utils.ThresholdSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { + return utils.ErrExists + }, + }, + } + rpcInternal := make(chan birpc.ClientConnector, 1) + rpcInternal <- ccM + cM := engine.NewConnManager(cfg) + cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal) + + rsPrf := &resourceProfile{ + ResourceProfile: &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: -1, + UsageTTL: time.Minute, + }, + } + rs := &resource{ + Resource: &utils.Resource{ + Tenant: "cgrates.org", + ID: "RES1", + Usages: map[string]*utils.ResourceUsage{ + "RU_Test": { + Tenant: "cgrates.org", + ID: "RU_Test", + Units: 4, + }, + }, + TTLIdx: []string{}, + }, + dirty: utils.BoolPointer(false), + tUsage: utils.Float64Pointer(10), + ttl: utils.DurationPointer(time.Minute), + rPrf: rsPrf, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) + if err != nil { + t.Error(err) + } + err = dm.SetResource(context.Background(), rs.Resource) + if err != nil { + t.Error(err) + } + + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, cM) + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + var reply string + var resources Resources + resources = append(resources, rs) + if _, err := resources.allocateResource(&utils.ResourceUsage{ + Tenant: "cgrates.org", + ID: "RU_ID", + Units: 1}, true); err != nil { + t.Error(err) + } + + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err != utils.ErrPartiallyExecuted { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrPartiallyExecuted, err) + } + + dm.DataDB().Flush(utils.EmptyString) +} + +func TestResourcesStoreResourceError(t *testing.T) { + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().StoreInterval = -1 + cfg.RPCConns()["test"] = &config.RPCConn{ + Conns: []*config.RemoteHost{{}}, + } + cfg.DataDbCfg().RplConns = []string{"test"} + dft := config.CgrConfig() + config.SetCgrConfig(cfg) + defer config.SetCgrConfig(dft) + + db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(db, cfg, engine.NewConnManager(cfg)) + + rS := NewResourceService(dm, cfg, engine.NewFilterS(cfg, nil, dm), nil) + + rsPrf := &utils.ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weights: utils.DynamicWeights{ + { + Weight: 10, + }}, + Limit: 10, + UsageTTL: time.Minute, + Stored: true, + } + + err := dm.SetResourceProfile(context.Background(), rsPrf, true) + if err != nil { + t.Fatal(err) + } + + args := &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{ + utils.OptsResourcesUsageID: "RU_Test", + utils.OptsResourcesUnits: 5, + utils.OptsResourcesUsageTTL: time.Minute, + }, + } + cfg.DataDbCfg().Items[utils.MetaResources].Replicate = true + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != utils.ErrDisconnected { + t.Error(err) + } + cfg.DataDbCfg().Items[utils.MetaResources].Replicate = false + + if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { + t.Error(err) + } else if reply != "Approved" { + t.Errorf("Unexpected reply returned: %q", reply) + } + + cfg.DataDbCfg().Items[utils.MetaResources].Replicate = true + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err != utils.ErrDisconnected { + t.Error(err) + } +} + +func TestResourcesV1ResourcesForEventErrRetrieveUsageID(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ + config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1ResourcesForEventErrRetrieveUsageTTL(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ + config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply Resources + if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AuthorizeResourcesErrRetrieveUsageID(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ + config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AuthorizeResourcesErrRetrieveUnits(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.Units = []*config.DynamicFloat64Opt{ + config.NewDynamicFloat64Opt([]string{"FLTR_Invalid"}, "*any", 3, nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AuthorizeResourcesErrRetrieveUsageTTL(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ + config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AllocateResourcesErrRetrieveUsageID(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ + config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AllocateResourcesErrRetrieveUsageTTL(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ + config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1AllocateResourcesErrRetrieveUnits(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.Units = []*config.DynamicFloat64Opt{ + config.NewDynamicFloat64Opt([]string{"FLTR_Invalid"}, "*any", 3, nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1ReleaseResourcesErrRetrieveUsageID(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ + config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} + +func TestResourcesV1ReleaseResourcesErrRetrieveUsageTTL(t *testing.T) { + tmp := engine.Cache + defer func() { + engine.Cache = tmp + }() + + engine.Cache.Clear(nil) + cfg := config.NewDefaultCGRConfig() + cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ + config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), + } + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + rS := NewResourceService(dm, cfg, fltrs, nil) + + args := &utils.CGREvent{ + ID: "ResourcesForEventTest", + Event: map[string]any{ + utils.AccountField: "1001", + }, + APIOpts: map[string]any{}, + } + + experr := `NOT_FOUND:FLTR_Invalid` + var reply string + if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || + err.Error() != experr { + t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) + } +} diff --git a/resources/resources.go b/resources/resources.go index 29791a517..d6256398c 100644 --- a/resources/resources.go +++ b/resources/resources.go @@ -589,355 +589,3 @@ func (rS *ResourceS) matchingResourcesForEvent(ctx *context.Context, tnt string, } return } - -// V1GetResourcesForEvent returns active resource configs matching the event -func (rS *ResourceS) V1GetResourcesForEvent(ctx *context.Context, args *utils.CGREvent, reply *Resources) (err error) { - if args == nil { - return utils.NewErrMandatoryIeMissing(utils.Event) - } - if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - - var usageID string - if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, - utils.OptsResourcesUsageID); err != nil { - return - } - - var ttl time.Duration - if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, - utils.OptsResourcesUsageTTL); err != nil { - return - } - usageTTL := utils.DurationPointer(ttl) - - if usageID == utils.EmptyString { - return utils.NewErrMandatoryIeMissing(utils.UsageID) - } - tnt := args.Tenant - if tnt == utils.EmptyString { - tnt = rS.cfg.GeneralCfg().DefaultTenant - } - - // RPC caching - if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1GetResourcesForEvent, utils.ConcatenatedKey(tnt, args.ID)) - refID := guardian.Guardian.GuardIDs("", - config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic - defer guardian.Guardian.UnguardIDs(refID) - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - cachedResp := itm.(*utils.CachedRPCResponse) - if cachedResp.Error == nil { - *reply = *cachedResp.Result.(*Resources) - } - return cachedResp.Error - } - defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: reply, Error: err}, - nil, true, utils.NonTransactional) - } - // end of RPC caching - - var mtcRLs Resources - if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, usageTTL); err != nil { - return err - } - *reply = mtcRLs - mtcRLs.unlock() - return -} - -// V1AuthorizeResources queries service to find if an Usage is allowed -func (rS *ResourceS) V1AuthorizeResources(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { - if args == nil { - return utils.NewErrMandatoryIeMissing(utils.Event) - } - if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - - var usageID string - if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, - utils.OptsResourcesUsageID); err != nil { - return - } - - var units float64 - if units, err = engine.GetFloat64Opts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.Units, - utils.OptsResourcesUnits); err != nil { - return - } - - var ttl time.Duration - if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, - utils.OptsResourcesUsageTTL); err != nil { - return - } - usageTTL := utils.DurationPointer(ttl) - - if usageID == utils.EmptyString { - return utils.NewErrMandatoryIeMissing(utils.UsageID) - } - - tnt := args.Tenant - if tnt == utils.EmptyString { - tnt = rS.cfg.GeneralCfg().DefaultTenant - } - - // RPC caching - if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AuthorizeResources, utils.ConcatenatedKey(tnt, args.ID)) - refID := guardian.Guardian.GuardIDs("", - config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic - defer guardian.Guardian.UnguardIDs(refID) - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - cachedResp := itm.(*utils.CachedRPCResponse) - if cachedResp.Error == nil { - *reply = *cachedResp.Result.(*string) - } - return cachedResp.Error - } - defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: reply, Error: err}, - nil, true, utils.NonTransactional) - } - // end of RPC caching - - var mtcRLs Resources - if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, usageTTL); err != nil { - return err - } - defer mtcRLs.unlock() - - var alcMessage string - if alcMessage, err = mtcRLs.allocateResource(&utils.ResourceUsage{ - Tenant: tnt, - ID: usageID, - Units: units}, true); err != nil { - if err == utils.ErrResourceUnavailable { - err = utils.ErrResourceUnauthorized - } - return - } - *reply = alcMessage - return -} - -// V1AllocateResources is called when a resource requires allocation -func (rS *ResourceS) V1AllocateResources(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { - if args == nil { - return utils.NewErrMandatoryIeMissing(utils.Event) - } - if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - - var usageID string - if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, - utils.OptsResourcesUsageID); err != nil { - return - } - - var units float64 - if units, err = engine.GetFloat64Opts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.Units, - utils.OptsResourcesUnits); err != nil { - return - } - - var ttl time.Duration - if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, - utils.OptsResourcesUsageTTL); err != nil { - return - } - usageTTL := utils.DurationPointer(ttl) - - if usageID == utils.EmptyString { - return utils.NewErrMandatoryIeMissing(utils.UsageID) - } - - tnt := args.Tenant - if tnt == utils.EmptyString { - tnt = rS.cfg.GeneralCfg().DefaultTenant - } - - // RPC caching - if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AllocateResources, utils.ConcatenatedKey(tnt, args.ID)) - refID := guardian.Guardian.GuardIDs("", - config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic - defer guardian.Guardian.UnguardIDs(refID) - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - cachedResp := itm.(*utils.CachedRPCResponse) - if cachedResp.Error == nil { - *reply = *cachedResp.Result.(*string) - } - return cachedResp.Error - } - defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: reply, Error: err}, - nil, true, utils.NonTransactional) - } - // end of RPC caching - - var mtcRLs Resources - if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, - usageTTL); err != nil { - return err - } - defer mtcRLs.unlock() - - var alcMsg string - if alcMsg, err = mtcRLs.allocateResource(&utils.ResourceUsage{Tenant: tnt, ID: usageID, - Units: units}, false); err != nil { - return - } - - // index it for storing - if err = rS.storeMatchedResources(ctx, mtcRLs); err != nil { - return - } - if err = rS.processThresholds(ctx, mtcRLs, args.APIOpts); err != nil { - return - } - *reply = alcMsg - return -} - -// V1ReleaseResources is called when we need to clear an allocation -func (rS *ResourceS) V1ReleaseResources(ctx *context.Context, args *utils.CGREvent, reply *string) (err error) { - if args == nil { - return utils.NewErrMandatoryIeMissing(utils.Event) - } - if missing := utils.MissingStructFields(args, []string{utils.ID, utils.Event}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - - var usageID string - if usageID, err = engine.GetStringOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageID, - utils.OptsResourcesUsageID); err != nil { - return - } - - var ttl time.Duration - if ttl, err = engine.GetDurationOpts(ctx, args.Tenant, args.AsDataProvider(), nil, rS.fltrS, rS.cfg.ResourceSCfg().Opts.UsageTTL, - utils.OptsResourcesUsageTTL); err != nil { - return - } - usageTTL := utils.DurationPointer(ttl) - - if usageID == utils.EmptyString { - return utils.NewErrMandatoryIeMissing(utils.UsageID) - } - - tnt := args.Tenant - if tnt == utils.EmptyString { - tnt = rS.cfg.GeneralCfg().DefaultTenant - } - - // RPC caching - if config.CgrConfig().CacheCfg().Partitions[utils.CacheRPCResponses].Limit != 0 { - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1ReleaseResources, utils.ConcatenatedKey(tnt, args.ID)) - refID := guardian.Guardian.GuardIDs("", - config.CgrConfig().GeneralCfg().LockingTimeout, cacheKey) // RPC caching needs to be atomic - defer guardian.Guardian.UnguardIDs(refID) - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - cachedResp := itm.(*utils.CachedRPCResponse) - if cachedResp.Error == nil { - *reply = *cachedResp.Result.(*string) - } - return cachedResp.Error - } - defer engine.Cache.Set(ctx, utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: reply, Error: err}, - nil, true, utils.NonTransactional) - } - // end of RPC caching - - var mtcRLs Resources - if mtcRLs, err = rS.matchingResourcesForEvent(ctx, tnt, args, usageID, - usageTTL); err != nil { - return - } - defer mtcRLs.unlock() - - if err = mtcRLs.clearUsage(usageID); err != nil { - return - } - - // Handle storing - if err = rS.storeMatchedResources(ctx, mtcRLs); err != nil { - return - } - if err = rS.processThresholds(ctx, mtcRLs, args.APIOpts); err != nil { - return - } - - *reply = utils.OK - return -} - -// V1GetResource returns a resource configuration -func (rS *ResourceS) V1GetResource(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.Resource) error { - if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - tnt := arg.Tenant - if tnt == utils.EmptyString { - tnt = rS.cfg.GeneralCfg().DefaultTenant - } - - // make sure resource is locked at process level - lkID := guardian.Guardian.GuardIDs(utils.EmptyString, - config.CgrConfig().GeneralCfg().LockingTimeout, - utils.ResourceLockKey(tnt, arg.ID)) - defer guardian.Guardian.UnguardIDs(lkID) - - res, err := rS.dm.GetResource(ctx, tnt, arg.ID, true, true, utils.NonTransactional) - if err != nil { - return err - } - *reply = *res - return nil -} - -func (rS *ResourceS) V1GetResourceWithConfig(ctx *context.Context, arg *utils.TenantIDWithAPIOpts, reply *utils.ResourceWithConfig) (err error) { - if missing := utils.MissingStructFields(arg, []string{utils.ID}); len(missing) != 0 { //Params missing - return utils.NewErrMandatoryIeMissing(missing...) - } - tnt := arg.Tenant - if tnt == utils.EmptyString { - tnt = rS.cfg.GeneralCfg().DefaultTenant - } - - // make sure resource is locked at process level - lkID := guardian.Guardian.GuardIDs(utils.EmptyString, - config.CgrConfig().GeneralCfg().LockingTimeout, - utils.ResourceLockKey(tnt, arg.ID)) - defer guardian.Guardian.UnguardIDs(lkID) - - var res *utils.Resource - res, err = rS.dm.GetResource(ctx, tnt, arg.ID, true, true, utils.NonTransactional) - if err != nil { - return - } - - // make sure resourceProfile is locked at process level - lkPrflID := guardian.Guardian.GuardIDs(utils.EmptyString, - config.CgrConfig().GeneralCfg().LockingTimeout, - utils.ResourceProfileLockKey(tnt, arg.ID)) - defer guardian.Guardian.UnguardIDs(lkPrflID) - - var cfg *utils.ResourceProfile - cfg, err = rS.dm.GetResourceProfile(ctx, tnt, arg.ID, true, true, utils.NonTransactional) - if err != nil { - return - } - - *reply = utils.ResourceWithConfig{ - Resource: res, - Config: cfg, - } - - return -} diff --git a/resources/resources_test.go b/resources/resources_test.go index 45e8914e1..60bcf2364 100644 --- a/resources/resources_test.go +++ b/resources/resources_test.go @@ -15,6 +15,7 @@ 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 resources import ( @@ -679,42 +680,6 @@ func TestRSCacheSetGet(t *testing.T) { t.Errorf("Expecting: %+v, received: %+v", r, x) } } -func TestResourceV1AuthorizeResourceMissingStruct(t *testing.T) { - var dmRES *engine.DataManager - cfg := config.NewDefaultCGRConfig() - - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dmRES = engine.NewDataManager(data, cfg, nil) - cfg.ResourceSCfg().StoreInterval = 1 - cfg.ResourceSCfg().StringIndexedFields = nil - cfg.ResourceSCfg().PrefixIndexedFields = nil - fltrs := engine.NewFilterS(cfg, nil, dmRES) - resService := NewResourceService(dmRES, cfg, - fltrs, nil) - var reply *string - argsMissingTenant := &utils.CGREvent{ - ID: "id1", - Event: map[string]any{}, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "test1", - utils.OptsResourcesUnits: 20, - }, - } - argsMissingUsageID := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "id1", - Event: map[string]any{}, - APIOpts: map[string]any{ - utils.OptsResourcesUnits: 20, - }, - } - if err := resService.V1AuthorizeResources(context.TODO(), argsMissingTenant, reply); err != nil && err.Error() != "MANDATORY_IE_MISSING: [Event]" { - t.Error(err.Error()) - } - if err := resService.V1AuthorizeResources(context.TODO(), argsMissingUsageID, reply); err != nil && err.Error() != "MANDATORY_IE_MISSING: [Event]" { - t.Error(err.Error()) - } -} func TestResourceAddResourceProfile(t *testing.T) { var dmRES *engine.DataManager @@ -2657,66 +2622,6 @@ func TestResourcesRecordUsageClearErr(t *testing.T) { } } -func TestResourceAllocateResourceOtherDB(t *testing.T) { - rProf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RL_DB", - FilterIDs: []string{"*string:~*opts.Resource:RL_DB"}, - Weights: utils.DynamicWeights{ - { - Weight: 100, - }}, - Limit: 2, - ThresholdIDs: []string{utils.MetaNone}, - UsageTTL: -time.Nanosecond, - } - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - idb, err := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - if err != nil { - t.Error(err) - } - dm := engine.NewDataManager(idb, cfg, nil) - fltS := engine.NewFilterS(cfg, nil, dm) - rs := NewResourceService(dm, cfg, fltS, nil) - if err := dm.SetResourceProfile(context.TODO(), rProf, true); err != nil { - t.Fatal(err) - } - if err := dm.SetResource(context.TODO(), &utils.Resource{ - Tenant: "cgrates.org", - ID: "RL_DB", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { // the resource in DB is expired (should be cleaned when the next allocate is called) - Tenant: "cgrates.org", - ID: "RU1", - ExpiryTime: time.Date(2014, 7, 3, 13, 43, 0, 1, time.UTC), - Units: 1, - }, - }, - TTLIdx: []string{"RU1"}, - }); err != nil { // simulate how the resource is stored in redis or mongo(non-exported fields are not populated) - t.Fatal(err) - } - var reply string - exp := rProf.ID - if err := rs.V1AllocateResources(context.TODO(), &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "ef0f554", - Event: map[string]any{"": ""}, - APIOpts: map[string]any{ - "Resource": "RL_DB", - utils.OptsResourcesUsageID: "56156434-2e44-4f16-a766-086f10b413cd", - utils.OptsResourcesUnits: 1, - }, - }, &reply); err != nil { - t.Fatal(err) - } else if reply != exp { - t.Errorf("Expected: %q, received: %q", exp, reply) - } - -} - func TestResourceClearUsageErr(t *testing.T) { tmpLogger := utils.Logger defer func() { @@ -3205,2355 +3110,6 @@ func TestResourcesProcessThresholdsThdConnMetaNone(t *testing.T) { } } -type ccMock struct { - calls map[string]func(ctx *context.Context, args any, reply any) error -} - -func (ccM *ccMock) Call(ctx *context.Context, serviceMethod string, args any, reply any) (err error) { - if call, has := ccM.calls[serviceMethod]; !has { - return rpcclient.ErrUnsupporteServiceMethod - } else { - return call(ctx, args, reply) - } -} - -func TestResourcesV1ResourcesForEventOK(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - TTLIdx: []string{}, - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_TEST1", - }, - } - - exp := Resources{ - { - Resource: &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - TTLIdx: []string{}, - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - }, - rPrf: &resourceProfile{ResourceProfile: rsPrf}, - ttl: utils.DurationPointer(72 * time.Hour), - }, - } - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(reply, exp) { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - utils.ToJSON(exp), utils.ToJSON(reply)) - } -} - -func TestResourcesV1ResourcesForEventNotFound(t *testing.T) { - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1002", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_TEST1", - }, - } - - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || - err.Error() != utils.ErrNotFound.Error() { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1ResourcesForEventMissingParameters(t *testing.T) { - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - experr := `MANDATORY_IE_MISSING: [Event]` - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), nil, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args := &utils.CGREvent{ - Tenant: "cgrates.org", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_TEST2", - }, - } - - experr = `MANDATORY_IE_MISSING: [ID]` - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args = &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "ResourcesForEventTest", - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_TEST3", - }, - } - - experr = `MANDATORY_IE_MISSING: [Event]` - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args = &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - } - - experr = `MANDATORY_IE_MISSING: [UsageID]` - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1ResourcesForEventCacheReplyExists(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1GetResourcesForEvent, - utils.ConcatenatedKey("cgrates.org", "ResourcesForEventTest")) - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_TEST1", - }, - } - - cacheReply := Resources{ - { - Resource: &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - }, - rPrf: rsPrf, - dirty: utils.BoolPointer(false), - tUsage: utils.Float64Pointer(10), - ttl: utils.DurationPointer(time.Minute), - }, - } - engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, - nil, true, utils.NonTransactional) - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(reply, cacheReply) { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - utils.ToJSON(cacheReply), utils.ToJSON(reply)) - } - - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1ResourcesForEventCacheReplySet(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1GetResourcesForEvent, - utils.ConcatenatedKey("cgrates.org", "ResourcesForEventTest")) - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_TEST1", - }, - } - - exp := &Resources{ - { - Resource: &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - }, - ttl: utils.DurationPointer(72 * time.Hour), - rPrf: &resourceProfile{ - ResourceProfile: rsPrf, - }, - }, - } - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(reply, *exp) { - t.Errorf("expected: <%v>, received: <%v>", exp, reply) - } - - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - resp := itm.(*utils.CachedRPCResponse) - if !reflect.DeepEqual(resp.Result, exp) { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ToJSON(exp), utils.ToJSON(resp.Result)) - } - } - - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1GetResourceOK(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - exp := utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - TTLIdx: []string{}, - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - } - - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{ - ID: "RES1", - }, - } - var reply utils.Resource - if err := rS.V1GetResource(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(reply, exp) { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - utils.ToJSON(exp), utils.ToJSON(reply)) - } -} - -func TestResourcesV1GetResourceNotFound(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{ - Tenant: "cgrates.org", - ID: "RES2", - }, - } - var reply utils.Resource - if err := rS.V1GetResource(context.Background(), args, &reply); err == nil || - err.Error() != utils.ErrNotFound.Error() { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1GetResourceMissingParameters(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{}, - } - - experr := `MANDATORY_IE_MISSING: [ID]` - var reply utils.Resource - if err := rS.V1GetResource(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1GetResourceWithConfigOK(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - exp := utils.ResourceWithConfig{ - Resource: &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - }, - Config: rsPrf, - } - - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{ - ID: "RES1", - }, - } - var reply utils.ResourceWithConfig - if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if !reflect.DeepEqual(reply, exp) { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - utils.ToJSON(exp), utils.ToJSON(reply)) - } -} - -func TestResourcesV1GetResourceWithConfigNilrPrfProfileNotFound(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES2", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{ - ID: "RES1", - }, - } - var reply utils.ResourceWithConfig - if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err == nil || - err != utils.ErrNotFound { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1GetResourceWithConfigResourceNotFound(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES2", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{ - ID: "RES1", - }, - } - var reply utils.ResourceWithConfig - if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err == nil || err != utils.ErrNotFound { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1GetResourceWithConfigMissingParameters(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - err := dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - experr := `MANDATORY_IE_MISSING: [ID]` - args := &utils.TenantIDWithAPIOpts{ - TenantID: &utils.TenantID{}, - } - var reply utils.ResourceWithConfig - if err := rS.V1GetResourceWithConfig(context.Background(), args, &reply); err == nil || err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AuthorizeResourcesOK(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } -} - -func TestResourcesV1AuthorizeResourcesNotAuthorized(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 0, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || - err != utils.ErrResourceUnauthorized { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrResourceUnauthorized, err) - } -} - -func TestResourcesV1AuthorizeResourcesNoMatch(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1002", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || - err != utils.ErrNotFound { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1AuthorizeResourcesNilCGREvent(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - experr := `MANDATORY_IE_MISSING: [Event]` - var reply string - - if err := rS.V1AuthorizeResources(context.Background(), nil, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AuthorizeResourcesMissingUsageID(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - experr := `MANDATORY_IE_MISSING: [UsageID]` - var reply string - - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AuthorizeResourcesCacheReplyExists(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AuthorizeResources, - utils.ConcatenatedKey("cgrates.org", "EventAuthorizeResource")) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - cacheReply := "Approved" - engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, - nil, true, utils.NonTransactional) - - var reply string - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != cacheReply { - t.Errorf("Unexpected reply returned: %q", reply) - } - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1AuthorizeResourcesCacheReplySet(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AuthorizeResources, - utils.ConcatenatedKey("cgrates.org", "EventAuthorizeResource")) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 4, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 2, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - var reply string - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } - - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - resp := itm.(*utils.CachedRPCResponse) - if *resp.Result.(*string) != "Approved" { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - "Approved", *resp.Result.(*string)) - } - } - - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1AllocateResourcesOK(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } -} - -func TestResourcesV1AllocateResourcesNoMatch(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - }, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1002", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err != utils.ErrNotFound { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1AllocateResourcesMissingParameters(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - experr := `MANDATORY_IE_MISSING: [UsageID]` - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args = &utils.CGREvent{ - ID: "EventAuthorizeResource", - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - experr = `MANDATORY_IE_MISSING: [Event]` - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args = &utils.CGREvent{ - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - experr = `MANDATORY_IE_MISSING: [ID]` - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - experr = `MANDATORY_IE_MISSING: [Event]` - if err := rS.V1AllocateResources(context.Background(), nil, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AllocateResourcesCacheReplyExists(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AllocateResources, - utils.ConcatenatedKey("cgrates.org", "EventAllocateResource")) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAllocateResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - cacheReply := "cacheApproved" - engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, - nil, true, utils.NonTransactional) - - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != cacheReply { - t.Errorf("Unexpected reply returned: %q", reply) - } - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1AllocateResourcesCacheReplySet(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1AllocateResources, - utils.ConcatenatedKey("cgrates.org", "EventAllocateResource")) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 4, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAllocateResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 2, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } - - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - resp := itm.(*utils.CachedRPCResponse) - if *resp.Result.(*string) != "Approved" { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - "Approved", *resp.Result.(*string)) - } - } - - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1AllocateResourcesResAllocErr(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err != utils.ErrResourceUnavailable { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrResourceUnavailable, err) - } -} - -func TestResourcesV1AllocateResourcesProcessThErr(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().StoreInterval = 2 - cfg.ResourceSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)} - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - ccM := &ccMock{ - calls: map[string]func(ctx *context.Context, args any, reply any) error{ - utils.ThresholdSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { - return utils.ErrExists - }, - }, - } - rpcInternal := make(chan birpc.ClientConnector, 1) - rpcInternal <- ccM - cM := engine.NewConnManager(cfg) - cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, cM) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err != utils.ErrPartiallyExecuted { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrPartiallyExecuted, err) - } - dm.DataDB().Flush(utils.EmptyString) -} - -func TestResourcesV1ReleaseResourcesOK(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } - - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != utils.OK { - t.Errorf("Unexpected reply returned: %q", reply) - } -} - -func TestResourcesV1ReleaseResourcesUsageNotFound(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: 0, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } - - args = &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test2", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - experr := `cannot find usage record with id: RU_Test2` - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1ReleaseResourcesNoMatch(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1002", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err != utils.ErrNotFound { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrNotFound, err) - } -} - -func TestResourcesV1ReleaseResourcesMissingParameters(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - - experr := `MANDATORY_IE_MISSING: [UsageID]` - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args = &utils.CGREvent{ - ID: "EventAuthorizeResource", - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - experr = `MANDATORY_IE_MISSING: [Event]` - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - args = &utils.CGREvent{ - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - experr = `MANDATORY_IE_MISSING: [ID]` - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - experr = `MANDATORY_IE_MISSING: [Event]` - if err := rS.V1ReleaseResources(context.Background(), nil, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1ReleaseResourcesCacheReplyExists(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1ReleaseResources, - utils.ConcatenatedKey("cgrates.org", "EventReleaseResource")) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 10, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - Tenant: "cgrates.org", - ID: "EventReleaseResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - cacheReply := "cacheReply" - engine.Cache.Set(context.Background(), utils.CacheRPCResponses, cacheKey, - &utils.CachedRPCResponse{Result: &cacheReply, Error: nil}, - nil, true, utils.NonTransactional) - - var reply string - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != cacheReply { - t.Errorf("Unexpected reply returned: %q", reply) - } - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1ReleaseResourcesCacheReplySet(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.CacheCfg().Partitions[utils.CacheRPCResponses].Limit = 1 - config.SetCgrConfig(cfg) - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - cacheKey := utils.ConcatenatedKey(utils.ResourceSv1ReleaseResources, - utils.ConcatenatedKey("cgrates.org", "EventReleaseResource")) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - } - rs := &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU1": { - Tenant: "cgrates.org", - ID: "RU1", - Units: 4, - }, - }, - TTLIdx: []string{}, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "EventReleaseResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 2, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - - var reply string - experr := `cannot find usage record with id: RU_Test` - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } - - if itm, has := engine.Cache.Get(utils.CacheRPCResponses, cacheKey); has { - resp := itm.(*utils.CachedRPCResponse) - if *resp.Result.(*string) != "" { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", - "", *resp.Result.(*string)) - } - } - - config.SetCgrConfig(config.NewDefaultCGRConfig()) -} - -func TestResourcesV1ReleaseResourcesProcessThErr(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().StoreInterval = 2 - cfg.ResourceSCfg().ThresholdSConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)} - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - ccM := &ccMock{ - calls: map[string]func(ctx *context.Context, args any, reply any) error{ - utils.ThresholdSv1ProcessEvent: func(ctx *context.Context, args, reply any) error { - return utils.ErrExists - }, - }, - } - rpcInternal := make(chan birpc.ClientConnector, 1) - rpcInternal <- ccM - cM := engine.NewConnManager(cfg) - cM.AddInternalConn(utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds), utils.ThresholdSv1, rpcInternal) - - rsPrf := &resourceProfile{ - ResourceProfile: &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaThresholds)}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: -1, - UsageTTL: time.Minute, - }, - } - rs := &resource{ - Resource: &utils.Resource{ - Tenant: "cgrates.org", - ID: "RES1", - Usages: map[string]*utils.ResourceUsage{ - "RU_Test": { - Tenant: "cgrates.org", - ID: "RU_Test", - Units: 4, - }, - }, - TTLIdx: []string{}, - }, - dirty: utils.BoolPointer(false), - tUsage: utils.Float64Pointer(10), - ttl: utils.DurationPointer(time.Minute), - rPrf: rsPrf, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf.ResourceProfile, true) - if err != nil { - t.Error(err) - } - err = dm.SetResource(context.Background(), rs.Resource) - if err != nil { - t.Error(err) - } - - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, cM) - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - var reply string - var resources Resources - resources = append(resources, rs) - if _, err := resources.allocateResource(&utils.ResourceUsage{ - Tenant: "cgrates.org", - ID: "RU_ID", - Units: 1}, true); err != nil { - t.Error(err) - } - - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err != utils.ErrPartiallyExecuted { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", utils.ErrPartiallyExecuted, err) - } - - dm.DataDB().Flush(utils.EmptyString) -} - -func TestResourcesStoreResourceError(t *testing.T) { - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().StoreInterval = -1 - cfg.RPCConns()["test"] = &config.RPCConn{ - Conns: []*config.RemoteHost{{}}, - } - cfg.DataDbCfg().RplConns = []string{"test"} - dft := config.CgrConfig() - config.SetCgrConfig(cfg) - defer config.SetCgrConfig(dft) - - db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(db, cfg, engine.NewConnManager(cfg)) - - rS := NewResourceService(dm, cfg, engine.NewFilterS(cfg, nil, dm), nil) - - rsPrf := &utils.ResourceProfile{ - Tenant: "cgrates.org", - ID: "RES1", - FilterIDs: []string{"*string:~*req.Account:1001"}, - ThresholdIDs: []string{utils.MetaNone}, - AllocationMessage: "Approved", - Weights: utils.DynamicWeights{ - { - Weight: 10, - }}, - Limit: 10, - UsageTTL: time.Minute, - Stored: true, - } - - err := dm.SetResourceProfile(context.Background(), rsPrf, true) - if err != nil { - t.Fatal(err) - } - - args := &utils.CGREvent{ - ID: "EventAuthorizeResource", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{ - utils.OptsResourcesUsageID: "RU_Test", - utils.OptsResourcesUnits: 5, - utils.OptsResourcesUsageTTL: time.Minute, - }, - } - cfg.DataDbCfg().Items[utils.MetaResources].Replicate = true - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != utils.ErrDisconnected { - t.Error(err) - } - cfg.DataDbCfg().Items[utils.MetaResources].Replicate = false - - if err := rS.V1AllocateResources(context.Background(), args, &reply); err != nil { - t.Error(err) - } else if reply != "Approved" { - t.Errorf("Unexpected reply returned: %q", reply) - } - - cfg.DataDbCfg().Items[utils.MetaResources].Replicate = true - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err != utils.ErrDisconnected { - t.Error(err) - } -} - func TestResourceMatchingResourcesForEventNotFoundInCache(t *testing.T) { cfg := config.NewDefaultCGRConfig() db, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) @@ -6125,338 +3681,7 @@ func TestResourcesMatchingResourcesForEventFinalCacheSetErr(t *testing.T) { } } -func TestResourcesV1ResourcesForEventErrRetrieveUsageID(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ - config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1ResourcesForEventErrRetrieveUsageTTL(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ - config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply Resources - if err := rS.V1GetResourcesForEvent(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AuthorizeResourcesErrRetrieveUsageID(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ - config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AuthorizeResourcesErrRetrieveUnits(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.Units = []*config.DynamicFloat64Opt{ - config.NewDynamicFloat64Opt([]string{"FLTR_Invalid"}, "*any", 3, nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AuthorizeResourcesErrRetrieveUsageTTL(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ - config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1AuthorizeResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AllocateResourcesErrRetrieveUsageID(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ - config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AllocateResourcesErrRetrieveUsageTTL(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ - config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1AllocateResourcesErrRetrieveUnits(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.Units = []*config.DynamicFloat64Opt{ - config.NewDynamicFloat64Opt([]string{"FLTR_Invalid"}, "*any", 3, nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1AllocateResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1ReleaseResourcesErrRetrieveUsageID(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageID = []*config.DynamicStringOpt{ - config.NewDynamicStringOpt([]string{"FLTR_Invalid"}, "*any", "value", nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - -func TestResourcesV1ReleaseResourcesErrRetrieveUsageTTL(t *testing.T) { - tmp := engine.Cache - defer func() { - engine.Cache = tmp - }() - - engine.Cache.Clear(nil) - cfg := config.NewDefaultCGRConfig() - cfg.ResourceSCfg().Opts.UsageTTL = []*config.DynamicDurationOpt{ - config.NewDynamicDurationOpt([]string{"FLTR_Invalid"}, "*any", time.Minute, nil), - } - data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) - dm := engine.NewDataManager(data, cfg, nil) - engine.Cache = engine.NewCacheS(cfg, dm, nil, nil) - fltrs := engine.NewFilterS(cfg, nil, dm) - rS := NewResourceService(dm, cfg, fltrs, nil) - - args := &utils.CGREvent{ - ID: "ResourcesForEventTest", - Event: map[string]any{ - utils.AccountField: "1001", - }, - APIOpts: map[string]any{}, - } - - experr := `NOT_FOUND:FLTR_Invalid` - var reply string - if err := rS.V1ReleaseResources(context.Background(), args, &reply); err == nil || - err.Error() != experr { - t.Errorf("expected: <%+v>, \nreceived: <%+v>", experr, err) - } -} - func TestResourceMatchingResourcesForEventWeightFromDynamicsErr(t *testing.T) { - defer func() { engine.Cache = engine.NewCacheS(config.NewDefaultCGRConfig(), nil, nil, nil) }()