github.com/omnigres/cli@v0.1.4/cmd/test.go (about)

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"database/sql"
     6  	"encoding/json"
     7  	"fmt"
     8  	cloudevents "github.com/cloudevents/sdk-go/v2"
     9  	"os"
    10  	"path"
    11  	"time"
    12  
    13  	"github.com/charmbracelet/log"
    14  	"github.com/omnigres/cli/orb"
    15  	"github.com/relvacode/iso8601"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  var testCmd = &cobra.Command{
    20  	Use:   "test",
    21  	Short: "Test orbs",
    22  	Run: func(cmd *cobra.Command, args []string) {
    23  		var cluster orb.OrbCluster
    24  		var err error
    25  		cluster, err = getOrbCluster()
    26  		if err != nil {
    27  			log.Fatal(err)
    28  		}
    29  
    30  		log.Debug("Workspace", "workspace", workspace)
    31  		cwd, err := os.Getwd()
    32  		if err != nil {
    33  			log.Fatal(err)
    34  		}
    35  
    36  		orbs, err := currentOrbs(cluster, cwd)
    37  		if err != nil {
    38  			log.Fatal(err)
    39  		}
    40  
    41  		ctx := context.Background()
    42  		log.Debug("Assembling orbs src", "orbs", orbs)
    43  
    44  		t := time.Now().Format("20060102150405")
    45  		nameForTestDatabase := func(orbName string) string {
    46  			return fmt.Sprintf("%s_%s_%s", orbName, "test", t)
    47  		}
    48  
    49  		err = testOrbs(ctx, cluster, orbs, nameForTestDatabase)
    50  
    51  		if err != nil {
    52  			log.Fatal(err)
    53  		}
    54  	},
    55  }
    56  
    57  func testOrbs(
    58  	ctx context.Context,
    59  	cluster orb.OrbCluster,
    60  	orbs []string,
    61  	databaseForOrb func(string) string,
    62  ) (err error) {
    63  
    64  	var testTarget, testRunner *sql.DB
    65  	testRunner, err = cluster.Connect(ctx, "omnigres")
    66  	if err != nil {
    67  		log.Error("Could not connect to orb. Ensure the docker container is running, perhaps 'omnigres start' will fix it.")
    68  		return
    69  	}
    70  
    71  	testOrb := func(orbName string) error {
    72  		dbName := databaseForOrb(orbName)
    73  		testTarget, err = cluster.Connect(ctx, dbName)
    74  		if err != nil {
    75  			return err
    76  		}
    77  		log.Debug("Testing orb", "orbName", orbName, "dbName", dbName)
    78  
    79  		_, err = testRunner.ExecContext(ctx, fmt.Sprintf(`create database %q`, dbName))
    80  		if err != nil {
    81  			log.Fatal(err)
    82  		}
    83  		_, err = testRunner.ExecContext(
    84  			ctx,
    85  			"update pg_database set datistemplate = true where datname = $1",
    86  			dbName,
    87  		)
    88  		if err != nil {
    89  			return err
    90  		}
    91  		cleanTestRunner := func() {
    92  			// remove istemplate so we can drop the database
    93  			_, err = testRunner.ExecContext(
    94  				ctx,
    95  				"update pg_database set datistemplate = false where datname = $1",
    96  				dbName,
    97  			)
    98  			if err != nil {
    99  				log.Fatal(err)
   100  			}
   101  
   102  			_, err = testRunner.ExecContext(
   103  				ctx,
   104  				fmt.Sprintf("drop database \"%s\"", dbName),
   105  			)
   106  			if err != nil {
   107  				log.Fatal(err)
   108  			}
   109  		}
   110  		defer cleanTestRunner()
   111  
   112  		orbSource := path.Join(orbName, "src")
   113  		assembleSchema(ctx, testRunner, orbSource, dbName)
   114  
   115  		_, err = testTarget.ExecContext(ctx, "create extension omni_test cascade")
   116  		if err != nil {
   117  			return err
   118  		}
   119  		testTarget.Close()
   120  
   121  		conn, err := testRunner.Conn(ctx)
   122  		if err != nil {
   123  			return err
   124  		}
   125  		defer conn.Close()
   126  
   127  		// assemble tests in target db
   128  		orbTestSource := path.Join(orbName, "tests")
   129  		assembleSchema(ctx, testRunner, orbTestSource, dbName)
   130  
   131  		// run tests
   132  		log.Infof("")
   133  		log.Infof("=== Running tests for %s ===", orbName)
   134  
   135  		err = setupCloudevents(ctx, conn)
   136  		if err != nil {
   137  			return err
   138  		}
   139  
   140  		testRows, err := conn.QueryContext(
   141  			ctx,
   142  			`select name, description, error_message from omni_test.run_tests($1)`,
   143  			dbName,
   144  		)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		defer testRows.Close()
   149  
   150  		for testRows.Next() {
   151  			var name, description, error_message sql.NullString
   152  			err = testRows.Scan(&name, &description, &error_message)
   153  			if err != nil {
   154  				return err
   155  			}
   156  		}
   157  		log.Info("===================================================================")
   158  
   159  		return nil
   160  	}
   161  
   162  	for _, orbName := range orbs {
   163  		err := testOrb(orbName)
   164  		if err != nil {
   165  			log.Error(err)
   166  		}
   167  	}
   168  	return
   169  }
   170  
   171  type testPassed struct {
   172  	Name        string       `json:"name"`
   173  	Description string       `json:"description"`
   174  	StartTime   iso8601.Time `json:"start_time"`
   175  	EndTime     iso8601.Time `json:"end_time"`
   176  }
   177  
   178  type testFailed struct {
   179  	Name        string       `json:"name"`
   180  	Description string       `json:"description"`
   181  	StartTime   iso8601.Time `json:"start_time"`
   182  	EndTime     iso8601.Time `json:"end_time"`
   183  	Error       string       `json:"error"`
   184  }
   185  
   186  func init() {
   187  	rootCmd.AddCommand(testCmd)
   188  
   189  	handler := cloudeventHandler{
   190  		Callback: func(e *cloudevents.Event) {
   191  			switch e.Type() {
   192  			case "org.omnigres.omni_test.test.passed.v1":
   193  				var msg testPassed
   194  				err := json.Unmarshal(e.Data(), &msg)
   195  				if err == nil {
   196  					log.Infof("✅ - %s (%s) [%s]", msg.Name, msg.Description, msg.EndTime.Sub(msg.StartTime.Time).String())
   197  				} else {
   198  					log.Error(err)
   199  				}
   200  			case "org.omnigres.omni_test.test.failed.v1":
   201  				var msg testFailed
   202  				err := json.Unmarshal(e.Data(), &msg)
   203  				if err == nil {
   204  					log.Infof("🔴 - %s (%s) [%s]: %s", msg.Name, msg.Description, msg.EndTime.Sub(msg.StartTime.Time).String(), msg.Error)
   205  				} else {
   206  					log.Error(err)
   207  				}
   208  			default:
   209  			}
   210  		},
   211  	}
   212  	cloudeventHandlers = append(cloudeventHandlers, handler)
   213  }