Skip to content

Commit

Permalink
minor enhancements to kwil testing tools (#886)
Browse files Browse the repository at this point in the history
  • Loading branch information
brennanjl authored Jul 22, 2024
1 parent a96d108 commit a2d2fd8
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 19 deletions.
54 changes: 49 additions & 5 deletions cmd/kwil-cli/cmds/utils/test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
Expand Down
59 changes: 45 additions & 14 deletions testing/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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{
Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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 {
Expand Down

0 comments on commit a2d2fd8

Please sign in to comment.