From 7d0b85a7cf158e6361addfc6e5a7d8898db7a0ef Mon Sep 17 00:00:00 2001 From: armirveliaj Date: Fri, 3 Oct 2025 10:24:10 -0400 Subject: [PATCH] add coverage tests on ips && accounts --- accounts/libaccounts_test.go | 127 +++++++++++++++++ ips/ips_test.go | 262 +++++++++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) create mode 100644 ips/ips_test.go diff --git a/accounts/libaccounts_test.go b/accounts/libaccounts_test.go index f1664065e..9a99877da 100644 --- a/accounts/libaccounts_test.go +++ b/accounts/libaccounts_test.go @@ -789,3 +789,130 @@ func TestMaxDebitAbstractFromConcretesInsufficientCredit(t *testing.T) { } } + +func TestRefundUnitsOnAccount(t *testing.T) { + acnt1 := &utils.Account{ + ID: "acc1", + Balances: map[string]*utils.Balance{}, + } + origBlnc1 := &utils.Balance{ + ID: "blnc1", + Units: &utils.Decimal{Big: decimal.New(10, 0)}, + } + acnt1.Balances["blnc1"] = origBlnc1 + + refund1 := &utils.Decimal{Big: decimal.New(5, 0)} + refundUnitsOnAccount(acnt1, refund1, origBlnc1) + + got1 := acnt1.Balances["blnc1"].Units.String() + want1 := "15" + if got1 != want1 { + t.Errorf("existing balance: expected %s, got %s", want1, got1) + } + + acnt2 := &utils.Account{ + ID: "acc2", + Balances: map[string]*utils.Balance{}, + } + origBlnc2 := &utils.Balance{ + ID: "blnc2", + Units: &utils.Decimal{Big: decimal.New(20, 0)}, + } + refund2 := &utils.Decimal{Big: decimal.New(7, 0)} + + refundUnitsOnAccount(acnt2, refund2, origBlnc2) + + got2 := acnt2.Balances["blnc2"].Units.String() + want2 := "7" + if got2 != want2 { + t.Errorf("new balance: expected %s, got %s", want2, got2) + } +} + +func TestUncompressUnits(t *testing.T) { + tests := []struct { + name string + units *utils.Decimal + cmprsFctr int + uf *utils.UnitFactor + want string + }{ + { + name: "NoCompressionNoFactor", + units: &utils.Decimal{Big: decimal.New(10, 0)}, + cmprsFctr: 1, + uf: nil, + want: "10", + }, + { + name: "WithCompression", + units: &utils.Decimal{Big: decimal.New(5, 0)}, + cmprsFctr: 3, + uf: nil, + want: "15", + }, + { + name: "WithUnitFactor", + units: &utils.Decimal{Big: decimal.New(4, 0)}, + cmprsFctr: 1, + uf: &utils.UnitFactor{Factor: &utils.Decimal{Big: decimal.New(2, 0)}}, + want: "8", + }, + { + name: "WithCompressionAndFactor", + units: &utils.Decimal{Big: decimal.New(2, 0)}, + cmprsFctr: 4, + uf: &utils.UnitFactor{Factor: &utils.Decimal{Big: decimal.New(5, 0)}}, + want: "40", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := uncompressUnits(tt.units, tt.cmprsFctr, nil, tt.uf).String() + if got != tt.want { + t.Errorf("expected %s, got %s", tt.want, got) + } + }) + } +} + +func TestNewBalanceOperator(t *testing.T) { + ctx := new(context.Context) + acntID := "1001" + filters := new(engine.FilterS) + + blncCfg := &utils.Balance{ID: "B1", Type: "unknown"} + bOp, err := newBalanceOperator(ctx, acntID, blncCfg, nil, filters, nil, nil, nil) + if err == nil { + t.Errorf("expected error for unsupported type, got nil") + } + if bOp != nil { + t.Errorf("expected nil operator, got %T", bOp) + } + + blncCfg = &utils.Balance{ID: "B2", Type: utils.MetaConcrete} + bOp, err = newBalanceOperator(ctx, acntID, blncCfg, nil, filters, nil, nil, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := bOp.(*concreteBalance); !ok { + t.Errorf("expected *concreteBalance, got %T", bOp) + } + if bOp.balanceCfg().ID != "B2" { + t.Errorf("expected balance ID B2, got %s", bOp.balanceCfg().ID) + } + + cncrt := &concreteBalance{} + blncCfg = &utils.Balance{ID: "B3", Type: utils.MetaAbstract} + bOp, err = newBalanceOperator(ctx, acntID, blncCfg, []*concreteBalance{cncrt}, filters, nil, nil, nil) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := bOp.(*abstractBalance); !ok { + t.Errorf("expected *abstractBalance, got %T", bOp) + } + if bOp.balanceCfg().ID != "B3" { + t.Errorf("expected balance ID B3, got %s", bOp.balanceCfg().ID) + } +} diff --git a/ips/ips_test.go b/ips/ips_test.go new file mode 100644 index 000000000..79f611953 --- /dev/null +++ b/ips/ips_test.go @@ -0,0 +1,262 @@ +/* +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 ips + +import ( + "testing" + + "github.com/cgrates/birpc/context" + "github.com/cgrates/cgrates/config" + "github.com/cgrates/cgrates/engine" + "github.com/cgrates/cgrates/utils" +) + +func TestStoreMatchedIPAllocations(t *testing.T) { + t.Run("StoreInterval is zero", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = 0 + + s := &IPService{ + cfg: cfg, + storedIPs: make(utils.StringSet), + } + + matched := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + ctx := context.Background() + err := s.storeMatchedIPAllocations(ctx, matched) + + if err != nil { + t.Errorf("Expected no error, got: %v", err) + } + + if len(s.storedIPs) != 0 { + t.Errorf("Expected storedIPs to be empty, got length: %d", len(s.storedIPs)) + } + }) + + t.Run("StoreInterval is positive, single allocation", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = 10 + + s := &IPService{ + cfg: cfg, + storedIPs: make(utils.StringSet), + } + + matched := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + ctx := context.Background() + err := s.storeMatchedIPAllocations(ctx, matched) + + if err != nil { + t.Errorf("Expected no error, got: %v", err) + } + + expectedTenantID := matched.TenantID() + if !s.storedIPs.Has(expectedTenantID) { + t.Errorf("Expected storedIPs to contain %q", expectedTenantID) + } + + if len(s.storedIPs) != 1 { + t.Errorf("Expected storedIPs length to be 1, got: %d", len(s.storedIPs)) + } + }) + + t.Run("StoreInterval is positive, multiple allocations", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = 10 + + s := &IPService{ + cfg: cfg, + storedIPs: make(utils.StringSet), + } + + matched1 := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + matched2 := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC2", + } + + ctx := context.Background() + + if err := s.storeMatchedIPAllocations(ctx, matched1); err != nil { + t.Errorf("Expected no error for first allocation, got: %v", err) + } + + if err := s.storeMatchedIPAllocations(ctx, matched2); err != nil { + t.Errorf("Expected no error for second allocation, got: %v", err) + } + + expectedTenantID := matched1.TenantID() + if !s.storedIPs.Has(expectedTenantID) { + t.Errorf("Expected storedIPs to contain %q", expectedTenantID) + } + }) + + t.Run("StoreInterval is negative, no DataManager", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = -1 + + s := &IPService{ + cfg: cfg, + dm: nil, + storedIPs: make(utils.StringSet), + } + + matched := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + ctx := context.Background() + err := s.storeMatchedIPAllocations(ctx, matched) + + if err == nil { + t.Error("Expected error when DataManager is nil, got nil") + } + + if err != utils.ErrNoDatabaseConn { + t.Errorf("Expected error %v, got: %v", utils.ErrNoDatabaseConn, err) + } + }) + + t.Run("StoreInterval is negative, with DataManager no DB", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = -1 + + dm := engine.NewDataManager(nil, cfg, nil) + + s := &IPService{ + cfg: cfg, + dm: dm, + storedIPs: make(utils.StringSet), + } + + matched := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + ctx := context.Background() + + defer func() { + if r := recover(); r == nil { + t.Error("Expected panic due to nil DataDB, got none") + } + }() + + _ = s.storeMatchedIPAllocations(ctx, matched) + }) + + t.Run("StoreInterval is negative, with working DataManager", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = -1 + + db, err := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + if err != nil { + t.Fatal(err) + } + dm := engine.NewDataManager(db, cfg, nil) + + s := &IPService{ + cfg: cfg, + dm: dm, + storedIPs: make(utils.StringSet), + } + + matched := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + ctx := context.Background() + err = s.storeMatchedIPAllocations(ctx, matched) + + if err != nil { + t.Errorf("Expected no error with working DataManager, got: %v", err) + } + }) + + t.Run("Nil context with StoreInterval zero", func(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + cfg.IPsCfg().StoreInterval = 0 + + s := &IPService{ + cfg: cfg, + storedIPs: make(utils.StringSet), + } + + matched := &utils.IPAllocations{ + Tenant: "cgrates.org", + ID: "ALLOC1", + } + + err := s.storeMatchedIPAllocations(nil, matched) + + if err != nil { + t.Errorf("Expected no error with nil context for StoreInterval=0, got: %v", err) + } + }) +} + +func TestNewIPService(t *testing.T) { + cfg := config.NewDefaultCGRConfig() + data, _ := engine.NewInternalDB(nil, nil, nil, cfg.DataDbCfg().Items) + dm := engine.NewDataManager(data, cfg, nil) + fltrs := engine.NewFilterS(cfg, nil, dm) + connMgr := engine.NewConnManager(cfg) + + svc := NewIPService(dm, cfg, fltrs, connMgr) + + if svc == nil { + t.Fatalf("expected non-nil IPService") + } + if svc.dm != dm { + t.Errorf("expected dm to be set, got %+v", svc.dm) + } + if svc.cfg != cfg { + t.Errorf("expected cfg to be set, got %+v", svc.cfg) + } + if svc.fltrs != fltrs { + t.Errorf("expected fltrs to be set, got %+v", svc.fltrs) + } + if svc.cm != connMgr { + t.Errorf("expected connMgr to be set, got %+v", svc.cm) + } + if svc.storedIPs == nil { + t.Errorf("expected storedIPs initialized") + } + if svc.stopBackup == nil { + t.Errorf("expected stopBackup channel initialized") + } + if svc.loopStopped == nil { + t.Errorf("expected loopStopped channel initialized") + } +}