From e228dda013d9178989e16a270aa47bc4c8ecace6 Mon Sep 17 00:00:00 2001 From: ionutboangiu Date: Sun, 22 Sep 2024 08:56:38 +0300 Subject: [PATCH] Improve test setup helpers - added hook support (executed after parsing config but before starting engine) - made db resets configurable - merged config parsing helper with the main Setup function - removed engineDelay parameter - replaced t.Log with t.Error for engine process kill error - improved option comments --- apis/attributes_it_test.go | 2 +- engine/libtest.go | 82 ++++++++++++++++++-------------------- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/apis/attributes_it_test.go b/apis/attributes_it_test.go index 026632cbe..61feac478 100644 --- a/apis/attributes_it_test.go +++ b/apis/attributes_it_test.go @@ -2258,7 +2258,7 @@ cgrates.org,ATTR_ARITH,,,,,,*req.MultiplyBetweenVariables,*multiply,~*req.Elem1; TpFiles: tpFiles, Encoding: *utils.Encoding, } - client, _ := testEnv.Setup(t, context.Background(), 0) + client, _ := testEnv.Setup(t, context.Background()) t.Run("SetAttributesThroughAPI", func(t *testing.T) { var reply string diff --git a/engine/libtest.go b/engine/libtest.go index 1c5f2a3dd..f5a821b62 100644 --- a/engine/libtest.go +++ b/engine/libtest.go @@ -317,6 +317,7 @@ func GetDefaultEmptyCacheStats() map[string]*ltcache.CacheStats { } } +// NewRPCClient establishes a connection to cgr-engine and returns the client. func NewRPCClient(cfg *config.ListenCfg, encoding string) (*birpc.Client, error) { switch encoding { case utils.MetaJSON: @@ -331,36 +332,52 @@ func NewRPCClient(cfg *config.ListenCfg, encoding string) (*birpc.Client, error) // 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) + ConfigPath string // path to the main configuration file + ConfigJSON string // configuration JSON content (used if ConfigPath is empty) + Encoding string // data encoding type (e.g., JSON, GOB) + LogBuffer io.Writer // captures log output of the test environment + PreserveDataDB bool // prevents automatic data_db flush when set + PreserveStorDB bool // prevents automatic stor_db flush when set + TpPath string // path to the tariff plans + TpFiles map[string]string // CSV data for tariff plans: filename -> content + + // PreStartHook executes custom logic relying on CGRConfig + // before starting cgr-engine. + PreStartHook func(*testing.T, *config.CGRConfig) } // 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) { +func (env TestEnvironment) Setup(t *testing.T, ctx *context.Context) (*birpc.Client, *config.CGRConfig) { t.Helper() - var cfg *config.CGRConfig + // Parse config files. + var cfgPath string 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) + case env.ConfigJSON != "": + cfgPath = t.TempDir() + filePath := filepath.Join(cfgPath, "cgrates.json") + if err := os.WriteFile(filePath, []byte(env.ConfigJSON), 0644); err != nil { + t.Fatal(err) } + case env.ConfigPath != "": + cfgPath = env.ConfigPath default: - cfg, env.ConfigPath = initCfg(t, ctx, env.ConfigJSON) + t.Fatal("missing config source") + } + cfg, err := config.NewCGRConfigFromPath(ctx, cfgPath) + if err != nil { + t.Fatalf("could not init config from path %s: %v", cfgPath, err) } - flushDBs(t, cfg, true, true) - startEngine(t, ctx, cfg, env.ConfigPath, engineDelay, env.LogBuffer) + flushDBs(t, cfg, !env.PreserveDataDB, !env.PreserveStorDB) + + if env.PreStartHook != nil { + env.PreStartHook(t, cfg) + } + startEngine(t, ctx, cfg, env.LogBuffer) client, err := NewRPCClient(cfg.ListenCfg(), env.Encoding) if err != nil { @@ -372,31 +389,10 @@ func (env TestEnvironment) Setup(t *testing.T, ctx *context.Context, engineDelay 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 { @@ -426,17 +422,16 @@ func flushDBs(t *testing.T, cfg *config.CGRConfig, flushDataDB, flushStorDB bool // 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) { +func startEngine(t *testing.T, ctx *context.Context, cfg *config.CGRConfig, logBuffer io.Writer) { t.Helper() binPath, err := exec.LookPath("cgr-engine") if err != nil { - t.Fatal("could not find cgr-engine executable") + t.Fatal(err) } engine := exec.CommandContext( ctx, binPath, - "-config_path", cfgPath, + "-config_path", cfg.ConfigPath, "-logger", utils.MetaStdLog, ) if logBuffer != nil { @@ -448,7 +443,7 @@ func startEngine(t *testing.T, ctx *context.Context, cfg *config.CGRConfig, cfgP } t.Cleanup(func() { if err := engine.Process.Kill(); err != nil { - t.Logf("failed to kill cgr-engine process (%d): %v", engine.Process.Pid, err) + t.Errorf("failed to kill cgr-engine process (%d): %v", engine.Process.Pid, err) } }) fib := utils.FibDuration(time.Millisecond, 0) @@ -461,5 +456,4 @@ func startEngine(t *testing.T, ctx *context.Context, cfg *config.CGRConfig, cfgP if err != nil { t.Fatalf("starting cgr-engine on port %s failed: %v", cfg.ListenCfg().RPCJSONListen, err) } - time.Sleep(time.Duration(waitEngine) * time.Millisecond) }