From b0c6e59740d2d2667c2a4243c11c697d757d8626 Mon Sep 17 00:00:00 2001 From: Trial97 Date: Tue, 20 Jul 2021 15:54:13 +0300 Subject: [PATCH] Added tests for resources --- engine/resources.go | 109 ++++---- engine/z_resources_test.go | 550 ++++++++++++++++++++++++++----------- 2 files changed, 442 insertions(+), 217 deletions(-) diff --git a/engine/resources.go b/engine/resources.go index 728990eeb..34bb9a315 100644 --- a/engine/resources.go +++ b/engine/resources.go @@ -121,16 +121,15 @@ func (ru *ResourceUsage) Clone() (cln *ResourceUsage) { // Resource represents a resource in the system // not thread safe, needs locking at process level type Resource struct { - Tenant string - ID string - Usages map[string]*ResourceUsage - TTLIdx []string // holds ordered list of ResourceIDs based on their TTL, empty if feature is disableda - lkID string // ID of the lock used when matching the resource - prflLkID string // ID of the lock used for the profile - ttl *time.Duration // time to leave for this resource, picked up on each Resource initialization out of config - tUsage *float64 // sum of all usages - dirty *bool // the usages were modified, needs save, *bool so we only save if enabled in config - rPrf *ResourceProfile // for ordering purposes + Tenant string + ID string + Usages map[string]*ResourceUsage + TTLIdx []string // holds ordered list of ResourceIDs based on their TTL, empty if feature is disableda + lkID string // ID of the lock used when matching the resource + ttl *time.Duration // time to leave for this resource, picked up on each Resource initialization out of config + tUsage *float64 // sum of all usages + dirty *bool // the usages were modified, needs save, *bool so we only save if enabled in config + rPrf *ResourceProfile // for ordering purposes } // resourceLockKey returns the ID used to lock a resource with guardian @@ -362,14 +361,9 @@ func (rs Resources) allocateResource(ru *ResourceUsage, dryRun bool) (alcMessage err = fmt.Errorf("empty configuration for resourceID: %s", r.TenantID()) return } - if r.rPrf.Limit >= r.TotalUsage()+ru.Units || r.rPrf.Limit == -1 { - if alcMessage == "" { - if r.rPrf.AllocationMessage != "" { - alcMessage = r.rPrf.AllocationMessage - } else { - alcMessage = r.rPrf.ID - } - } + if alcMessage == utils.EmptyString && + (r.rPrf.Limit >= r.TotalUsage()+ru.Units || r.rPrf.Limit == -1) { + alcMessage = utils.FirstNonEmpty(r.rPrf.AllocationMessage, r.rPrf.ID) } } if alcMessage == "" { @@ -493,6 +487,31 @@ func (rS *ResourceService) storeResource(r *Resource) (err error) { return } +// storeMatchedResources will store the list of resources based on the StoreInterval +func (rS *ResourceService) storeMatchedResources(mtcRLs Resources) (err error) { + if rS.cgrcfg.ResourceSCfg().StoreInterval == 0 { + return + } + if rS.cgrcfg.ResourceSCfg().StoreInterval > 0 { + rS.srMux.Lock() + defer rS.srMux.Unlock() + } + for _, r := range mtcRLs { + if r.dirty != nil { + *r.dirty = true // mark it to be saved + if rS.cgrcfg.ResourceSCfg().StoreInterval > 0 { + rS.storedResources.Add(r.TenantID()) + continue + } + if err = rS.storeResource(r); err != nil { + return + } + } + + } + return +} + // processThresholds will pass the event for resource to ThresholdS func (rS *ResourceService) processThresholds(rs Resources, opts map[string]interface{}) (err error) { if len(rS.cgrcfg.ResourceSCfg().ThresholdSConns) == 0 { @@ -545,7 +564,6 @@ func (rS *ResourceService) processThresholds(rs Resources, opts map[string]inter // matchingResourcesForEvent returns ordered list of matching resources which are active by the time of the call func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREvent, evUUID string, usageTTL *time.Duration) (rs Resources, err error) { - matchingResources := make(map[string]*Resource) var rIDs utils.StringSet evNm := utils.MapStorage{ utils.MetaReq: ev.Event, @@ -583,6 +601,7 @@ func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREv return } } + rs = make(Resources, 0, len(rIDs)) for resName := range rIDs { lkPrflID := guardian.Guardian.GuardIDs("", config.CgrConfig().GeneralCfg().LockingTimeout, @@ -594,6 +613,7 @@ func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREv if err == utils.ErrNotFound { continue } + rs.unlock() return } rPrf.lock(lkPrflID) @@ -605,6 +625,7 @@ func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREv if pass, err := rS.filterS.Pass(tnt, rPrf.FilterIDs, evNm); err != nil { rPrf.unlock() + rs.unlock() return nil, err } else if !pass { rPrf.unlock() @@ -616,7 +637,8 @@ func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREv r, err := rS.dm.GetResource(rPrf.Tenant, rPrf.ID, true, true, "") if err != nil { guardian.Guardian.UnguardIDs(lkID) - guardian.Guardian.UnguardIDs(lkPrflID) + rPrf.unlock() + rs.unlock() return nil, err } r.lock(lkID) // pass the lock into resource so we have it as reference @@ -631,17 +653,12 @@ func (rS *ResourceService) matchingResourcesForEvent(tnt string, ev *utils.CGREv r.ttl = utils.DurationPointer(rPrf.UsageTTL) } r.rPrf = rPrf - matchingResources[rPrf.ID] = r + rs = append(rs, r) } - if len(matchingResources) == 0 { + if len(rs) == 0 { return nil, utils.ErrNotFound } - // All good, convert from Map to Slice so we can sort - rs = make(Resources, 0, len(matchingResources)) - for _, r := range matchingResources { - rs = append(rs, r) - } rs.Sort() for i, r := range rs { if r.rPrf.Blocker && i != len(rs)-1 { // blocker will stop processing and we are not at last index @@ -803,21 +820,8 @@ func (rS *ResourceService) V1AllocateResources(args utils.ArgRSv1ResourceUsage, } // index it for storing - for _, r := range mtcRLs { - if rS.cgrcfg.ResourceSCfg().StoreInterval != 0 && r.dirty != nil { - if rS.cgrcfg.ResourceSCfg().StoreInterval == -1 { - *r.dirty = true - if err = rS.storeResource(r); err != nil { - return - } - } else { - *r.dirty = true // mark it to be saved - rS.srMux.Lock() - rS.storedResources.Add(r.TenantID()) - rS.srMux.Unlock() - } - } - + if err = rS.storeMatchedResources(mtcRLs); err != nil { + return } if err = rS.processThresholds(mtcRLs, args.APIOpts); err != nil { return @@ -863,7 +867,7 @@ func (rS *ResourceService) V1ReleaseResources(args utils.ArgRSv1ResourceUsage, r var mtcRLs Resources if mtcRLs, err = rS.matchingResourcesForEvent(tnt, args.CGREvent, args.UsageID, args.UsageTTL); err != nil { - return err + return } defer mtcRLs.unlock() @@ -872,24 +876,9 @@ func (rS *ResourceService) V1ReleaseResources(args utils.ArgRSv1ResourceUsage, r } // Handle storing - if rS.cgrcfg.ResourceSCfg().StoreInterval != -1 { - rS.srMux.Lock() - defer rS.srMux.Unlock() + if err = rS.storeMatchedResources(mtcRLs); err != nil { + return } - for _, r := range mtcRLs { - if r.dirty != nil { - if rS.cgrcfg.ResourceSCfg().StoreInterval == -1 { - if err = rS.storeResource(r); err != nil { - return - } - } else { - *r.dirty = true // mark it to be saved - rS.storedResources.Add(r.TenantID()) - } - } - - } - if err = rS.processThresholds(mtcRLs, args.APIOpts); err != nil { return } diff --git a/engine/z_resources_test.go b/engine/z_resources_test.go index 5cf012579..cf5bff444 100644 --- a/engine/z_resources_test.go +++ b/engine/z_resources_test.go @@ -2617,166 +2617,18 @@ func TestResourceMatchWithIndexFalse(t *testing.T) { } func TestResourceCaching(t *testing.T) { - var dmRES *DataManager - defaultCfg := config.NewDefaultCGRConfig() - data := NewInternalDB(nil, nil, true) - dmRES = NewDataManager(data, config.CgrConfig().CacheCfg(), nil) - defaultCfg.ResourceSCfg().StoreInterval = 1 - defaultCfg.ResourceSCfg().StringIndexedFields = nil - defaultCfg.ResourceSCfg().PrefixIndexedFields = nil - resService := NewResourceService(dmRES, defaultCfg, - &FilterS{dm: dmRES, cfg: defaultCfg}, nil) - if err != nil { - t.Errorf("Error: %+v", err) - } - fltrRes1 := &Filter{ - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "FLTR_RES_1", - Rules: []*FilterRule{ - { - Type: utils.MetaString, - Element: "~*req.Resources", - Values: []string{"ResourceProfile1"}, - }, - { - Type: utils.MetaGreaterOrEqual, - Element: "~*req.UsageInterval", - Values: []string{(time.Second).String()}, - }, - { - Type: utils.MetaGreaterOrEqual, - Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage, - Values: []string{(time.Second).String()}, - }, - { - Type: utils.MetaGreaterOrEqual, - Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight, - Values: []string{"9.0"}, - }, - }, - } - dmRES.SetFilter(fltrRes1, true) - fltrRes2 := &Filter{ - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "FLTR_RES_2", - Rules: []*FilterRule{ - { - Type: utils.MetaString, - Element: "~*req.Resources", - Values: []string{"ResourceProfile2"}, - }, - { - Type: utils.MetaGreaterOrEqual, - Element: "~*req.PddInterval", - Values: []string{(time.Second).String()}, - }, - { - Type: utils.MetaGreaterOrEqual, - Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Usage, - Values: []string{(time.Second).String()}, - }, - { - Type: utils.MetaGreaterOrEqual, - Element: utils.DynamicDataPrefix + utils.MetaReq + utils.NestingSep + utils.Weight, - Values: []string{"15.0"}, - }, - }, - } - dmRES.SetFilter(fltrRes2, true) - fltrRes3 := &Filter{ - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "FLTR_RES_3", - Rules: []*FilterRule{ - { - Type: utils.MetaPrefix, - Element: "~*req.Resources", - Values: []string{"ResourceProfilePrefix"}, - }, - }, - } - dmRES.SetFilter(fltrRes3, true) - resprf := []*ResourceProfile{ - { - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "ResourceProfile1", - FilterIDs: []string{"FLTR_RES_1"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - }, - UsageTTL: 10 * time.Second, - Limit: 10.00, - AllocationMessage: "AllocationMessage", - Weight: 20.00, - ThresholdIDs: []string{""}, - }, - { - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "ResourceProfile2", // identifier of this resource - FilterIDs: []string{"FLTR_RES_2"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - }, - UsageTTL: 10 * time.Second, - Limit: 10.00, - AllocationMessage: "AllocationMessage", - Weight: 20.00, - ThresholdIDs: []string{""}, - }, - { - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "ResourceProfile3", - FilterIDs: []string{"FLTR_RES_3"}, - ActivationInterval: &utils.ActivationInterval{ - ActivationTime: time.Date(2014, 7, 14, 14, 25, 0, 0, time.UTC), - }, - UsageTTL: 10 * time.Second, - Limit: 10.00, - AllocationMessage: "AllocationMessage", - Weight: 20.00, - ThresholdIDs: []string{""}, - }, - } - resourceTest := Resources{ - { - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "ResourceProfile1", - Usages: map[string]*ResourceUsage{}, - TTLIdx: []string{}, - rPrf: resprf[0], - }, - { - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "ResourceProfile2", - Usages: map[string]*ResourceUsage{}, - TTLIdx: []string{}, - rPrf: resprf[1], - }, - { - Tenant: config.CgrConfig().GeneralCfg().DefaultTenant, - ID: "ResourceProfile3", - Usages: map[string]*ResourceUsage{}, - TTLIdx: []string{}, - rPrf: resprf[2], - }, - } - - for _, resProfile := range resprf { - dmRES.SetResourceProfile(resProfile, true) - } - for _, res := range resourceTest { - dmRES.SetResource(res) - } //clear the cache Cache.Clear(nil) + // start fresh with new dataManager + cfg := config.NewDefaultCGRConfig() + dm := NewDataManager(NewInternalDB(nil, nil, true), config.CgrConfig().CacheCfg(), nil) - dmRES = NewDataManager(data, config.CgrConfig().CacheCfg(), nil) - - defaultCfg.ResourceSCfg().StoreInterval = 1 - defaultCfg.ResourceSCfg().StringIndexedFields = nil - defaultCfg.ResourceSCfg().PrefixIndexedFields = nil - resService = NewResourceService(dmRES, defaultCfg, - &FilterS{dm: dmRES, cfg: defaultCfg}, nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dm, cfg, + &FilterS{dm: dm, cfg: cfg}, nil) if err != nil { t.Errorf("Error: %+v", err) } @@ -2822,7 +2674,7 @@ func TestResourceCaching(t *testing.T) { "Destination": "3002"}, } - mres, err := resService.matchingResourcesForEvent(ev.Tenant, ev, + mres, err := rS.matchingResourcesForEvent(ev.Tenant, ev, "TestResourceCaching", nil) if err != nil { t.Errorf("Error: %+v", err) @@ -6304,3 +6156,387 @@ func TestResourcesV1ReleaseResourcesProcessThErr(t *testing.T) { dm.DataDB().Flush(utils.EmptyString) } + +func TestResourcesStoreResourceError(t *testing.T) { + 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 := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, cfg.CacheCfg(), NewConnManager(cfg, make(map[string]chan rpcclient.ClientConnector))) + + rS := NewResourceService(dm, cfg, NewFilterS(cfg, nil, dm), nil) + + rsPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES1", + FilterIDs: []string{"*string:~*req.Account:1001"}, + ThresholdIDs: []string{utils.MetaNone}, + AllocationMessage: "Approved", + Weight: 10, + Limit: 10, + UsageTTL: time.Minute, + Stored: true, + } + + err := dm.SetResourceProfile(rsPrf, true) + if err != nil { + t.Fatal(err) + } + + args := utils.ArgRSv1ResourceUsage{ + CGREvent: &utils.CGREvent{ + ID: "EventAuthorizeResource", + Event: map[string]interface{}{ + utils.AccountField: "1001", + }, + }, + UsageID: "RU_Test", + UsageTTL: utils.DurationPointer(time.Minute), + Units: 5, + } + cfg.DataDbCfg().Items[utils.MetaResources].Replicate = true + var reply string + if err := rS.V1AllocateResources(args, &reply); err != utils.ErrDisconnected { + t.Error(err) + } + cfg.DataDbCfg().Items[utils.MetaResources].Replicate = false + + if err := rS.V1AllocateResources(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(args, &reply); err != utils.ErrDisconnected { + t.Error(err) + } +} + +func TestResourceMatchingResourcesForEventNotFoundInCache(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dmRES := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dmRES, cfg, + &FilterS{dm: dmRES, cfg: cfg}, nil) + + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventNotFoundInCache", nil, nil, true, utils.NonTransactional) + _, err := rS.matchingResourcesForEvent("cgrates.org", new(utils.CGREvent), + "TestResourceMatchingResourcesForEventNotFoundInCache", utils.DurationPointer(10*time.Second)) + if err != utils.ErrNotFound { + t.Errorf("Error: %+v", err) + } +} + +func TestResourceMatchingResourcesForEventNotFoundInDB(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dmRES := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dmRES, cfg, + &FilterS{dm: dmRES, cfg: cfg}, nil) + + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventNotFoundInDB", utils.StringSet{"Res2": {}}, nil, true, utils.NonTransactional) + _, err := rS.matchingResourcesForEvent("cgrates.org", new(utils.CGREvent), + "TestResourceMatchingResourcesForEventNotFoundInDB", utils.DurationPointer(10*time.Second)) + if err != utils.ErrNotFound { + t.Errorf("Error: %+v", err) + } +} + +func TestResourceMatchingResourcesForEventLocks(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dm, cfg, + &FilterS{dm: dm, cfg: cfg}, nil) + + prfs := make([]*ResourceProfile, 0) + ids := utils.StringSet{} + for i := 0; i < 10; i++ { + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: fmt.Sprintf("RES%d", i), + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: 20.00, + ThresholdIDs: []string{utils.MetaNone}, + } + dm.SetResourceProfile(rPrf, true) + prfs = append(prfs, rPrf) + ids.Add(rPrf.ID) + } + dm.RemoveResource("cgrates.org", "RES1") + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks", ids, nil, true, utils.NonTransactional) + _, err := rS.matchingResourcesForEvent("cgrates.org", new(utils.CGREvent), + "TestResourceMatchingResourcesForEventLocks", utils.DurationPointer(10*time.Second)) + if err != utils.ErrNotFound { + t.Errorf("Error: %+v", err) + } + for _, rPrf := range prfs { + if rPrf.isLocked() { + t.Fatalf("Expected profile to not be locked %q", rPrf.ID) + } + if rPrf.ID == "RES1" { + continue + } + if r, err := dm.GetResource(rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil { + t.Errorf("error %s for <%s>", err, rPrf.ID) + } else if r.isLocked() { + t.Fatalf("Expected resource to not be locked %q", rPrf.ID) + } + } + +} + +func TestResourceMatchingResourcesForEventLocks2(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dm, cfg, + &FilterS{dm: dm, cfg: cfg}, nil) + + prfs := make([]*ResourceProfile, 0) + ids := utils.StringSet{} + for i := 0; i < 10; i++ { + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: fmt.Sprintf("RES%d", i), + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: 20.00, + ThresholdIDs: []string{utils.MetaNone}, + } + dm.SetResourceProfile(rPrf, true) + prfs = append(prfs, rPrf) + ids.Add(rPrf.ID) + } + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES20", + FilterIDs: []string{"FLTR_RES_201"}, + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: 20.00, + ThresholdIDs: []string{utils.MetaNone}, + } + err = db.SetResourceProfileDrv(rPrf) + if err != nil { + t.Fatal(err) + } + prfs = append(prfs, rPrf) + ids.Add(rPrf.ID) + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks2", ids, nil, true, utils.NonTransactional) + _, err := rS.matchingResourcesForEvent("cgrates.org", new(utils.CGREvent), + "TestResourceMatchingResourcesForEventLocks2", utils.DurationPointer(10*time.Second)) + expErr := utils.ErrPrefixNotFound(rPrf.FilterIDs[0]) + if err == nil || err.Error() != expErr.Error() { + t.Errorf("Expected error: %s ,received: %+v", expErr, err) + } + for _, rPrf := range prfs { + if rPrf.isLocked() { + t.Fatalf("Expected profile to not be locked %q", rPrf.ID) + } + if rPrf.ID == "RES20" { + continue + } + if r, err := dm.GetResource(rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil { + t.Errorf("error %s for <%s>", err, rPrf.ID) + } else if r.isLocked() { + t.Fatalf("Expected resource to not be locked %q", rPrf.ID) + } + } +} + +func TestResourceMatchingResourcesForEventLocksBlocker(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dm, cfg, + &FilterS{dm: dm, cfg: cfg}, nil) + + prfs := make([]*ResourceProfile, 0) + ids := utils.StringSet{} + for i := 0; i < 10; i++ { + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: fmt.Sprintf("RES%d", i), + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: float64(10 - i), + Blocker: i == 4, + ThresholdIDs: []string{utils.MetaNone}, + } + dm.SetResourceProfile(rPrf, true) + prfs = append(prfs, rPrf) + ids.Add(rPrf.ID) + } + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocksBlocker", ids, nil, true, utils.NonTransactional) + mres, err := rS.matchingResourcesForEvent("cgrates.org", new(utils.CGREvent), + "TestResourceMatchingResourcesForEventLocksBlocker", utils.DurationPointer(10*time.Second)) + if err != nil { + t.Errorf("Error: %+v", err) + } + defer mres.unlock() + if len(mres) != 5 { + t.Fatal("Expected 6 resources") + } + for _, rPrf := range prfs[5:] { + if rPrf.isLocked() { + t.Errorf("Expected profile to not be locked %q", rPrf.ID) + } + if r, err := dm.GetResource(rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil { + t.Errorf("error %s for <%s>", err, rPrf.ID) + } else if r.isLocked() { + t.Fatalf("Expected resource to not be locked %q", rPrf.ID) + } + } + for _, rPrf := range prfs[:5] { + if !rPrf.isLocked() { + t.Errorf("Expected profile to be locked %q", rPrf.ID) + } + if r, err := dm.GetResource(rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil { + t.Errorf("error %s for <%s>", err, rPrf.ID) + } else if !r.isLocked() { + t.Fatalf("Expected resource to be locked %q", rPrf.ID) + } + } +} + +func TestResourceMatchingResourcesForEventLocksActivationInterval(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + db := NewInternalDB(nil, nil, true) + dm := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dm, cfg, + &FilterS{dm: dm, cfg: cfg}, nil) + + ids := utils.StringSet{} + for i := 0; i < 10; i++ { + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: fmt.Sprintf("RES%d", i), + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: 20.00, + ThresholdIDs: []string{utils.MetaNone}, + } + dm.SetResourceProfile(rPrf, true) + ids.Add(rPrf.ID) + } + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: "RES21", + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: 20.00, + ThresholdIDs: []string{utils.MetaNone}, + ActivationInterval: &utils.ActivationInterval{ + ExpiryTime: time.Now().Add(-5 * time.Second), + }, + } + dm.SetResourceProfile(rPrf, true) + ids.Add(rPrf.ID) + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks2", ids, nil, true, utils.NonTransactional) + mres, err := rS.matchingResourcesForEvent("cgrates.org", &utils.CGREvent{Time: utils.TimePointer(time.Now())}, + "TestResourceMatchingResourcesForEventLocks2", utils.DurationPointer(10*time.Second)) + if err != nil { + t.Errorf("Error: %+v", err) + } + defer mres.unlock() + if rPrf.isLocked() { + t.Fatalf("Expected profile to not be locked %q", rPrf.ID) + } + if r, err := dm.GetResource(rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil { + t.Errorf("error %s for <%s>", err, rPrf.ID) + } else if r.isLocked() { + t.Fatalf("Expected resource to not be locked %q", rPrf.ID) + } +} + +func TestResourceMatchingResourcesForEventLocks3(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + prfs := make([]*ResourceProfile, 0) + Cache.Clear(nil) + db := &DataDBMock{ + GetResourceProfileDrvF: func(tnt, id string) (*ResourceProfile, error) { + if id == "RES1" { + return nil, utils.ErrNotImplemented + } + rPrf := &ResourceProfile{ + Tenant: "cgrates.org", + ID: id, + UsageTTL: 10 * time.Second, + Limit: 10.00, + AllocationMessage: "AllocationMessage", + Weight: 20.00, + ThresholdIDs: []string{utils.MetaNone}, + } + Cache.Set(utils.CacheResources, rPrf.TenantID(), &Resource{ + Tenant: rPrf.Tenant, + ID: rPrf.ID, + Usages: make(map[string]*ResourceUsage), + }, nil, true, utils.NonTransactional) + prfs = append(prfs, rPrf) + return rPrf, nil + }, + } + dm := NewDataManager(db, config.CgrConfig().CacheCfg(), nil) + cfg.ResourceSCfg().StoreInterval = 1 + cfg.ResourceSCfg().StringIndexedFields = nil + cfg.ResourceSCfg().PrefixIndexedFields = nil + rS := NewResourceService(dm, cfg, + &FilterS{dm: dm, cfg: cfg}, nil) + + ids := utils.StringSet{} + for i := 0; i < 10; i++ { + ids.Add(fmt.Sprintf("RES%d", i)) + } + Cache.Set(utils.CacheEventResources, "TestResourceMatchingResourcesForEventLocks3", ids, nil, true, utils.NonTransactional) + _, err := rS.matchingResourcesForEvent("cgrates.org", new(utils.CGREvent), + "TestResourceMatchingResourcesForEventLocks3", utils.DurationPointer(10*time.Second)) + if err != utils.ErrNotImplemented { + t.Errorf("Error: %+v", err) + } + for _, rPrf := range prfs { + if rPrf.isLocked() { + t.Fatalf("Expected profile to not be locked %q", rPrf.ID) + } + // if r, err := dm.GetResource(rPrf.Tenant, rPrf.ID, true, false, utils.NonTransactional); err != nil { + // t.Errorf("error %s for <%s>", err, rPrf.ID) + // } else if r.isLocked() { + // t.Fatalf("Expected resource to not be locked %q", rPrf.ID) + // } + } + +}