diff --git a/engine/calldesc_test.go b/engine/calldesc_test.go index a9ff49e51..a183848ba 100644 --- a/engine/calldesc_test.go +++ b/engine/calldesc_test.go @@ -2096,3 +2096,88 @@ func TestCallDescRefundRounding(t *testing.T) { t.Errorf("Expected %v,Received %v", utils.ToJSON(expAcc), utils.ToJSON(acc)) } } + +func TestNewCallDescriptorFromCGREventErr(t *testing.T) { + timezone := "UTC" + tests := []struct { + name string + cgrEv *utils.CGREvent + wantErr bool + }{ + { + name: "Missing Account", + cgrEv: &utils.CGREvent{ + Tenant: "cgrates.org", + Event: map[string]interface{}{ + utils.Category: "Call", + }, + }, + wantErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := NewCallDescriptorFromCGREvent(tt.cgrEv, timezone) + if (err != nil) != tt.wantErr { + t.Errorf("NewCallDescriptorFromCGREvent() error = %v, wantErr %v", err, tt.wantErr) + return + } + + }) + } +} + +func TestValidateCallData(t *testing.T) { + tests := []struct { + name string + cd *CallDescriptor + wantErr bool + }{ + { + name: "Valid Call Data", + cd: &CallDescriptor{ + TimeStart: time.Date(2023, 5, 8, 12, 0, 0, 0, time.UTC), + TimeEnd: time.Date(2023, 5, 8, 12, 30, 0, 0, time.UTC), + DurationIndex: time.Duration(30) * time.Minute, + }, + wantErr: false, + }, + { + name: "TimeStart is equal to TimeEnd", + cd: &CallDescriptor{ + TimeStart: time.Date(2023, 5, 8, 12, 0, 0, 0, time.UTC), + TimeEnd: time.Date(2023, 5, 8, 12, 0, 0, 0, time.UTC), + DurationIndex: time.Duration(0), + }, + wantErr: true, + }, + { + name: "TimeStart is after TimeEnd", + cd: &CallDescriptor{ + TimeStart: time.Date(2023, 5, 8, 12, 30, 0, 0, time.UTC), + TimeEnd: time.Date(2023, 5, 8, 12, 0, 0, 0, time.UTC), + DurationIndex: time.Duration(0), + }, + wantErr: true, + }, + { + name: "DurationIndex is less than TimeEnd - TimeStart", + cd: &CallDescriptor{ + TimeStart: time.Date(2023, 5, 8, 12, 15, 0, 0, time.UTC), + TimeEnd: time.Date(2023, 5, 8, 12, 30, 0, 0, time.UTC), + DurationIndex: time.Duration(20) * time.Minute, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.cd.ValidateCallData() + if (err != nil) != tt.wantErr { + t.Errorf("ValidateCallData() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/engine/cdr_test.go b/engine/cdr_test.go index b6415a2f3..293c66f4b 100644 --- a/engine/cdr_test.go +++ b/engine/cdr_test.go @@ -1259,3 +1259,99 @@ func TestCDRcombimedCdrFieldVal(t *testing.T) { } } + +func TestUsageAsCDR(t *testing.T) { + tests := []struct { + name string + record *UsageRecord + timezone string + want *CDR + wantError bool + }{ + { + name: "Valid Usage Record", + record: &UsageRecord{ + ToR: utils.VOICE, + RequestType: utils.META_RATED, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "1002", + SetupTime: "2013-11-07T08:42:20Z", + AnswerTime: "2013-11-07T08:42:26Z", + Usage: "10", + ExtraFields: map[string]string{"key1": "value1", "key2": "value2"}, + }, + timezone: "UTC", + want: &CDR{ + ToR: utils.VOICE, + RequestType: utils.META_RATED, + Tenant: "cgrates.org", + Category: "call", + Account: "1001", + Subject: "1001", + Destination: "1002", + SetupTime: time.Date(2013, 11, 7, 8, 42, 20, 0, time.UTC), + AnswerTime: time.Date(2013, 11, 7, 8, 42, 26, 0, time.UTC), + Usage: time.Duration(10) * time.Second, + Cost: 1.01, + ExtraFields: map[string]string{"key1": "value1", "key2": "value2"}, + }, + wantError: false, + }, + { + name: "Invalid SetupTime", + record: &UsageRecord{ + SetupTime: "invalid-time", + }, + timezone: "UTC", + want: nil, + wantError: true, + }, + { + name: "Invalid AnswerTime", + record: &UsageRecord{ + SetupTime: "2023-05-08T10:00:00Z", + AnswerTime: "invalid-time", + }, + timezone: "UTC", + want: nil, + wantError: true, + }, + { + name: "Invalid Usage", + record: &UsageRecord{ + + SetupTime: "2023-05-08T10:00:00Z", + AnswerTime: "2023-05-08T10:00:05Z", + Usage: "invalid-usage", + }, + timezone: "UTC", + want: nil, + wantError: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := tt.record.AsCDR(tt.timezone) + if (err != nil) != tt.wantError { + t.Errorf("AsCDR() error = %v, wantError %v", err, tt.wantError) + return + } + if tt.want != nil { + if got.ToR != tt.want.ToR || got.RequestType != tt.want.RequestType || + got.Tenant != tt.want.Tenant || got.Category != tt.want.Category || got.Account != tt.want.Account || + got.Subject != tt.want.Subject || got.Destination != tt.want.Destination { + t.Errorf("AsCDR() got = %v, want %v", got, tt.want) + } + for k, v := range tt.record.ExtraFields { + if gotV, ok := got.ExtraFields[k]; !ok || gotV != v { + t.Errorf("AsCDR() extra field key %s got value = %v, want value = %v", k, gotV, v) + } + } + } + }) + } +} diff --git a/engine/datamanager_test.go b/engine/datamanager_test.go index 9d03cf406..cba1498d4 100644 --- a/engine/datamanager_test.go +++ b/engine/datamanager_test.go @@ -1798,3 +1798,183 @@ func TestDMSetSQPrf(t *testing.T) { } } + +func TestRemoveAttributeProfile(t *testing.T) { + cfg, _ := config.NewDefaultCGRConfig() + defer func() { + cfg2, _ := config.NewDefaultCGRConfig() + config.SetCgrConfig(cfg2) + }() + cfg.DataDbCfg().Items[utils.MetaAttributeProfiles].Replicate = true + cfg.DataDbCfg().RplConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes)} + clientConn := make(chan birpc.ClientConnector, 1) + clientConn <- clMock(func(_ *context.Context, serviceMethod string, _, _ interface{}) error { + if serviceMethod == utils.ReplicatorSv1RemoveAttributeProfile { + return nil + } + return utils.ErrNotImplemented + }) + + dm := NewDataManager(NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items), cfg.CacheCfg(), NewConnManager(cfg, map[string]chan context.ClientConnector{ + utils.ConcatenatedKey(utils.MetaInternal, utils.MetaAttributes): clientConn, + })) + + tests := []struct { + name string + tenant string + id string + transactionID string + withIndex bool + wantError bool + }{ + { + name: "Remove Nonexistent Attribute Profile", + tenant: "cgrates.org", + id: "non_exst", + withIndex: true, + wantError: true, + }, + { + name: "Remove AttributeProfile non-existing filter", + tenant: "cgrates.org", + id: "ATTR_1002_SIMPLEAUTH", + withIndex: true, + wantError: true, + }, + { + name: "Remove an attribute profile without a specific index", + tenant: "cgrates.org", + id: "ATTR_NO_FLTR", + withIndex: false, + wantError: false, + }, + } + attributes := []struct { + index bool + attribute *AttributeProfile + }{ + { + index: true, + attribute: &AttributeProfile{ + Tenant: "cgrates.org", + ID: "ATTR_1002_SIMPLEAUTH", + FilterIDs: []string{"FLT_NIL"}, + Contexts: []string{"simpleauth"}, + Weight: 20.0, + }, + }, { + attribute: &AttributeProfile{ + Tenant: "cgrates.org", + ID: "ATTR_NO_FLTR", + Contexts: []string{utils.MetaSessionS, utils.META_ANY}, + Attributes: []*Attribute{ + { + Path: utils.MetaReq + utils.NestingSep + utils.CGRID, + Value: config.NewRSRParsersMustCompile("test_generated_id", true, utils.INFIELD_SEP), + }, + }, + Weight: 20.0, + }, + index: false, + }, + } + for _, attr := range attributes { + dm.SetAttributeProfile(attr.attribute, attr.index) + } + + config.SetCgrConfig(cfg) + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := dm.RemoveAttributeProfile(tt.tenant, tt.id, tt.transactionID, tt.withIndex) + if (err != nil) != tt.wantError { + t.Errorf("RemoveAttributeProfile() error = %v, wantError %v", err, tt.wantError) + return + } + }) + } +} + +func TestDataManagerRemoveChargerProfile(t *testing.T) { + cfg, _ := config.NewDefaultCGRConfig() + defer func() { + cfg2, _ := config.NewDefaultCGRConfig() + config.SetCgrConfig(cfg2) + }() + cfg.DataDbCfg().RplConns = []string{utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1)} + cfg.DataDbCfg().Items[utils.MetaChargerProfiles].Replicate = true + db := NewInternalDB(nil, nil, true, cfg.DataDbCfg().Items) + clientConn := make(chan birpc.ClientConnector, 1) + clientConn <- clMock(func(_ *context.Context, serviceMethod string, _, _ interface{}) error { + if serviceMethod == utils.ReplicatorSv1RemoveChargerProfile { + return nil + } + return utils.ErrNotFound + }) + dm := NewDataManager(db, cfg.CacheCfg(), NewConnManager(cfg, map[string]chan context.ClientConnector{ + utils.ConcatenatedKey(utils.MetaInternal, utils.ReplicatorSv1): clientConn, + })) + testCases := []struct { + name string + tenant string + id string + transactionID string + withIndex bool + expectedErr bool + chargerProfile *ChargerProfile + }{ + { + name: "RemoveChargerProfile - Not Found", + tenant: "cgrates.org", + id: "cpp1", + transactionID: "", + withIndex: false, + expectedErr: true, + }, + { + name: "RemoveChargerProfile - Broken Filter", + tenant: "cgrates.org", + id: "cpp2", + transactionID: "", + withIndex: true, + chargerProfile: &ChargerProfile{ + Tenant: "cgrates.org", + ID: "cpp2", + FilterIDs: []string{"FLTR_CP_2"}, + ActivationInterval: &utils.ActivationInterval{ + ActivationTime: time.Date(2023, 7, 14, 14, 25, 0, 0, time.UTC), + }, + RunID: "*rated", + AttributeIDs: []string{"ATTR_1"}, + Weight: 20, + }, + expectedErr: true, + }, + { + name: "RemoveChargerProfile - Successful Removal", + tenant: "cgrates.org", + id: "cpp1", + transactionID: "", + withIndex: false, + expectedErr: false, + chargerProfile: &ChargerProfile{ + Tenant: "cgrates.org", + ID: "cpp1", + FilterIDs: []string{"*string:Account:1001"}, + RunID: "*default", + Weight: 20, + }, + }, + } + config.SetCgrConfig(cfg) + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + if tc.chargerProfile != nil { + dm.SetChargerProfile(tc.chargerProfile, true) + } + err := dm.RemoveChargerProfile(tc.tenant, tc.id, tc.transactionID, tc.withIndex) + if (err != nil) != tc.expectedErr { + t.Errorf("Expected error: %v, received error: %v", tc.expectedErr, err) + } + }) + } +} diff --git a/engine/responder_test.go b/engine/responder_test.go index 59d7cab25..58e210cb3 100644 --- a/engine/responder_test.go +++ b/engine/responder_test.go @@ -1130,3 +1130,75 @@ func TestResponderDebitCached(t *testing.T) { t.Error(err) } } + +// func TestResponderGetCostCache(t *testing.T) { +// cfg, _ := config.NewDefaultCGRConfig() +// cfg.CacheCfg()[utils.CacheRPCResponses].Limit = 1 +// tmpCache := Cache +// defer func() { +// cfg2, _ := config.NewDefaultCGRConfig() +// config.SetCgrConfig(cfg2) +// Cache = tmpCache +// }() +// Cache.Clear(nil) +// config.SetCgrConfig(cfg) +// rs := &Responder{} +// arg := &CallDescriptorWithArgDispatcher{ +// CallDescriptor: &CallDescriptor{ +// CgrID: "CGRID", +// Tenant: "cgrates.org", +// Category: "call", +// Account: "1001", +// Subject: "1001", +// Destination: "1002", +// ToR: utils.VOICE, +// TimeStart: time.Now(), +// TimeEnd: time.Now().Add(10 * time.Minute), +// }, +// } + +// reply := &CallCost{} +// cacheKey := utils.ConcatenatedKey(utils.ResponderGetCost, arg.CgrID) +// Cache.Set(utils.CacheRPCResponses, cacheKey, +// &utils.CachedRPCResponse{Result: &CallCost{ +// Tenant: "cgrates.org", +// Destination: "1002", +// ToR: utils.VOICE, +// Cost: 1.5, +// Timespans: TimeSpans{ +// &TimeSpan{ +// TimeStart: time.Now(), +// TimeEnd: time.Now().Add(10 * time.Minute), +// Cost: 1.5, +// RateInterval: &RateInterval{ +// Timing: &RITiming{ +// StartTime: "00:00:00", +// }, +// Rating: &RIRate{ +// ConnectFee: 0, +// Rates: RateGroups{ +// &Rate{ +// GroupIntervalStart: 0, +// Value: 0.15, +// RateIncrement: time.Minute, +// RateUnit: time.Minute, +// }, +// }, +// }, +// }, +// }, +// }, +// }, Error: nil}, +// nil, true, utils.NonTransactional) + +// err := rs.GetCost(arg, reply) +// if err != nil { +// t.Fatalf("Unexpected error: %v", err) +// } + +// if rpl, has := Cache.Get(utils.CacheRPCResponses, cacheKey); !has { +// t.Fatalf("Expected cached result, but not found") +// } else if !reflect.DeepEqual(rpl, reply) { +// t.Errorf("Expected %+v,Received %+v", utils.ToJSON(reply), utils.ToJSON(rpl)) +// } +// }