From a2d2fd856badc18f96aafd9ce3c4535156e03708 Mon Sep 17 00:00:00 2001 From: Brennan Lamey <66885902+brennanjl@users.noreply.github.com> Date: Mon, 22 Jul 2024 10:08:20 -0500 Subject: [PATCH] minor enhancements to kwil testing tools (#886) --- cmd/kwil-cli/cmds/utils/test.go | 54 +++++++++++++++++++++++++++--- testing/testing.go | 59 +++++++++++++++++++++++++-------- 2 files changed, 94 insertions(+), 19 deletions(-) diff --git a/cmd/kwil-cli/cmds/utils/test.go b/cmd/kwil-cli/cmds/utils/test.go index 97c1a5988..7753718eb 100644 --- a/cmd/kwil-cli/cmds/utils/test.go +++ b/cmd/kwil-cli/cmds/utils/test.go @@ -8,10 +8,13 @@ import ( "strings" "github.com/kwilteam/kwil-db/cmd/common/display" + "github.com/kwilteam/kwil-db/cmd/kwil-cli/cmds/common" "github.com/kwilteam/kwil-db/core/log" "github.com/kwilteam/kwil-db/internal/sql/pg" "github.com/kwilteam/kwil-db/testing" + "github.com/manifoldco/promptui" "github.com/spf13/cobra" + "go.uber.org/zap" ) var ( @@ -54,12 +57,24 @@ func testCmd() *cobra.Command { Long: testLong, Example: testExample, RunE: func(cmd *cobra.Command, args []string) error { - l := log.New(log.Config{ - Level: log.InfoLevel.String(), - Format: log.FormatPlain, - }) + + confg := zap.NewProductionConfig() + confg.EncoderConfig.TimeKey = "" + confg.EncoderConfig.EncodeTime = nil + confg.Encoding = "console" + confg.DisableCaller = true + + l, err := confg.Build() + if err != nil { + return display.PrintErr(cmd, err) + } + + l2 := log.Logger{ + L: l, + } + opts := testing.Options{ - Logger: testing.LoggerFromKwilLogger(&l), + Logger: testing.LoggerFromKwilLogger(&l2), } userHasSetPgConn := false @@ -98,6 +113,34 @@ func testCmd() *cobra.Command { } } + opts.ReplaceExistingContainer = func() (bool, error) { + //if common + assumeYes, err := common.GetAssumeYesFlag(cmd) + if err != nil { + return false, err + } + + if assumeYes { + return true, nil + } + + sel := promptui.Prompt{ + Label: fmt.Sprintf(`Existing Docker contains found with name "%s". Wipe the existing container and create a new one? (y/n)`, testing.ContainerName), + Default: "N", + } + + res, err := sel.Run() + if err != nil { + return false, err + } + + if res == "Y" || res == "y" { + return true, nil + } + + return false, nil + } + // run the tests for _, path := range testCases { _, err := expandHome(&path) @@ -140,6 +183,7 @@ func testCmd() *cobra.Command { cmd.Flags().StringVar(&pass, "password", "", "password for the database user") cmd.Flags().StringVar(&host, "host", "localhost", "host of the database") cmd.Flags().StringVar(&port, "port", "5432", "port of the database") + common.BindAssumeYesFlag(cmd) return cmd } diff --git a/testing/testing.go b/testing/testing.go index 255e8eef9..a8e2c6dba 100644 --- a/testing/testing.go +++ b/testing/testing.go @@ -151,7 +151,7 @@ func (tc SchemaTest) Run(ctx context.Context, opts *Options) error { // each test case is named after the index it is for its type. // It is run in a function to allow defers err := func() error { - logger.Logf(`running test "%s"`, testFnIdentifiers[i]) + logger.Logf(`running test %s`, testFnIdentifiers[i]) // setup a tx and execution engine outerTx, err := d.BeginPreparedTx(ctx) @@ -293,7 +293,7 @@ func (e *TestCase) runExecution(ctx context.Context, platform *Platform) error { dbid := utils.GenerateDBID(e.Database, deployer) // log to help users debug failed tests - platform.Logger.Logf(`executing action/procedure "%s" against schema "%s" (DBID: "%s")`, e.Target, e.Database, dbid) + platform.Logger.Logf(`executing action/procedure "%s" against schema "%s" (DBID: %s)`, e.Target, e.Database, dbid) res, err := platform.Engine.Procedure(ctx, platform.DB, &common.ExecutionData{ TransactionData: common.TransactionData{ @@ -415,25 +415,43 @@ func runWithPostgres(ctx context.Context, opts *Options, fn func(context.Context return fn(ctx, db, opts.Logger) } - // check if the user has docker - err = exec.Command(ctx, "docker") - if err != nil { - if strings.Contains(err.Error(), "not found") { + port := "52853" // random port + + // Run the container + bts, err := exec.CommandOutput(ctx, startCommand(port)...) + switch { + case err == nil: + // do nothing + case strings.Contains(err.Error(), "command not found"): + { return fmt.Errorf("docker not found. Please ensure Docker is installed and running") } - return fmt.Errorf("error checking for Docker installation: %w", err) - } + case strings.Contains(err.Error(), "Conflict. The container name") && opts.ReplaceExistingContainer != nil: + // check if the container is in use + use, err := opts.ReplaceExistingContainer() + if err != nil { + return err + } - port := "52853" // random port + if !use { + return fmt.Errorf(`cannot create test-container: conflicting container name: "%s"`, ContainerName) + } - // Run the container - bts, err := exec.CommandOutput(ctx, "docker", "run", "-d", "-p", fmt.Sprintf("%s:5432", port), "--name", "kwil-testing-postgres", "-e", - "POSTGRES_HOST_AUTH_METHOD=trust", "kwildb/postgres:latest") - if err != nil { + err = exec.Command(ctx, "docker", "rm", "-f", ContainerName) + if err != nil { + return fmt.Errorf("error removing conflicting container: %w", err) + } + + bts, err = exec.CommandOutput(ctx, startCommand(port)...) + if err != nil { + return fmt.Errorf("error running test container: %w", err) + } + default: return fmt.Errorf("error running test container: %w", err) } + defer func() { - err2 := exec.Command(ctx, "docker", "rm", "-f", "kwil-testing-postgres") + err2 := exec.Command(ctx, "docker", "rm", "-f", ContainerName) if err2 != nil { if err == nil { err = err2 @@ -455,6 +473,14 @@ func runWithPostgres(ctx context.Context, opts *Options, fn func(context.Context return fn(ctx, db, opts.Logger) } +// ContainerName is the name of the test container +const ContainerName = "kwil-testing-postgres" + +// startCommand returns the docker start command +func startCommand(port string) []string { + return []string{"docker", "run", "-d", "-p", fmt.Sprintf("%s:5432", port), "--name", ContainerName, "-e", "POSTGRES_HOST_AUTH_METHOD=trust", "kwildb/postgres:latest"} +} + // connectWithRetry tries to connect to Postgres, and will retry n times at // 1 second intervals if it fails. func connectWithRetry(ctx context.Context, port string, n int) (*pg.DB, error) { @@ -499,6 +525,11 @@ type Options struct { Conn *pg.ConnConfig // Logger is a logger to be used in the test Logger Logger + // ReplaceExistingContainer is a callback function that is called when + // a conflicting container name is already in use. If it returns + // true, then the container will be removed and recreated. If it + // returns false, then the test will fail. + ReplaceExistingContainer func() (bool, error) } func (d *Options) valid() error {