Add the test suite

Similar to the one from v0.11. Has different csv loading logic and passes
context.
This commit is contained in:
ionutboangiu
2024-08-14 18:30:49 +03:00
committed by Dan Christian Bogos
parent 0dfb3b860d
commit 9d59e88552

View File

@@ -26,8 +26,10 @@ import (
"os"
"os/exec"
"path"
"path/filepath"
"slices"
"strings"
"testing"
"time"
"github.com/cgrates/birpc"
@@ -317,3 +319,139 @@ func NewRPCClient(cfg *config.ListenCfg, encoding string) (*birpc.Client, error)
return nil, errors.New("invalid encoding")
}
}
// TestEnvironment holds the setup parameters and configurations
// required for running integration tests.
type TestEnvironment struct {
Name string // usually the name of the test
ConfigPath string // file path to the main configuration file
ConfigJSON string // contains the configuration JSON content if ConfigPath is missing
TpPath string // specifies the path to the tariff plans
TpFiles map[string]string // maps CSV filenames to their content for tariff plan loading
LogBuffer io.Writer // captures the log output of the test environment
Encoding string // specifies the data encoding type (e.g., JSON, GOB)
}
// Setup initializes the testing environment using the provided configuration. It loads the configuration
// from a specified path or creates a new one if the path is not provided. The method starts the engine,
// establishes an RPC client connection, and loads CSV data if provided. It returns an RPC client and the
// configuration.
func (env TestEnvironment) Setup(t *testing.T, ctx *context.Context, engineDelay int) (*birpc.Client, *config.CGRConfig) {
t.Helper()
var cfg *config.CGRConfig
switch {
case env.ConfigPath != "":
var err error
cfg, err = config.NewCGRConfigFromPath(ctx, env.ConfigPath)
if err != nil {
t.Fatalf("failed to init config from path %s: %v", env.ConfigPath, err)
}
default:
cfg, env.ConfigPath = initCfg(t, ctx, env.ConfigJSON)
}
flushDBs(t, cfg, true, true)
startEngine(t, ctx, cfg, env.ConfigPath, engineDelay, env.LogBuffer)
client, err := NewRPCClient(cfg.ListenCfg(), env.Encoding)
if err != nil {
t.Fatalf("could not connect to cgr-engine: %v", err)
}
loadCSVs(t, env.TpPath, env.TpFiles)
return client, cfg
}
// initCfg creates a new CGRConfig from the provided configuration content string.
// It generates a temporary directory and file path, writes the content to the configuration
// file, and returns the created CGRConfig and the configuration directory path.
func initCfg(t *testing.T, ctx *context.Context, cfgContent string) (cfg *config.CGRConfig, cfgPath string) {
t.Helper()
if cfgContent == utils.EmptyString {
t.Fatal("ConfigJSON is required but empty")
}
cfgPath = t.TempDir()
filePath := filepath.Join(cfgPath, "cgrates.json")
if err := os.WriteFile(filePath, []byte(cfgContent), 0644); err != nil {
t.Fatal(err)
}
cfg, err := config.NewCGRConfigFromPath(ctx, cfgPath)
if err != nil {
t.Fatalf("failed to init config from path %s: %v", cfgPath, err)
}
return cfg, cfgPath
}
// loadCSVs loads tariff plan data from CSV files. The CSV files are created based on the csvFiles map, where
// the key represents the file name and the value the contains its contents. Assumes the data is loaded
// automatically (RunDelay != 0)
func loadCSVs(t *testing.T, tpPath string, csvFiles map[string]string) {
t.Helper()
if tpPath != "" {
for fileName, content := range csvFiles {
filePath := path.Join(tpPath, fileName)
if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
t.Fatalf("could not write to file %s: %v", filePath, err)
}
}
}
}
// flushDBs resets the databases specified in the configuration if the corresponding flags are true.
func flushDBs(t *testing.T, cfg *config.CGRConfig, flushDataDB, flushStorDB bool) {
t.Helper()
if flushDataDB {
if err := InitDataDB(cfg); err != nil {
t.Fatalf("failed to flush %s dataDB: %v", cfg.DataDbCfg().Type, err)
}
}
if flushStorDB {
if err := InitStorDB(cfg); err != nil {
t.Fatalf("failed to flush %s storDB: %v", cfg.StorDbCfg().Type, err)
}
}
}
// startEngine starts the CGR engine process with the provided configuration. It writes engine logs to the provided
// logBuffer (if any) and waits for the engine to be ready. If the passed context were to be cancelled, the engine
// would also shut down.
func startEngine(t *testing.T, ctx *context.Context, cfg *config.CGRConfig, cfgPath string, waitEngine int,
logBuffer io.Writer) {
t.Helper()
binPath, err := exec.LookPath("cgr-engine")
if err != nil {
t.Fatal("could not find cgr-engine executable")
}
engine := exec.CommandContext(
ctx,
binPath,
"-config_path", cfgPath,
"-logger", utils.MetaStdLog,
)
if logBuffer != nil {
engine.Stdout = logBuffer
engine.Stderr = logBuffer
}
if err := engine.Start(); err != nil {
t.Fatalf("cgr-engine command failed: %v", err)
}
t.Cleanup(func() {
if err := engine.Process.Kill(); err != nil {
t.Logf("failed to kill cgr-engine process (%d): %v", engine.Process.Pid, err)
}
})
fib := utils.FibDuration(time.Millisecond, 0)
for i := 0; i < 16; i++ {
time.Sleep(fib())
if _, err := jsonrpc.Dial(utils.TCP, cfg.ListenCfg().RPCJSONListen); err == nil {
break
}
}
if err != nil {
t.Fatalf("starting cgr-engine on port %s failed: %v", cfg.ListenCfg().RPCJSONListen, err)
}
time.Sleep(time.Duration(waitEngine) * time.Millisecond)
}