github.com/JarrahG/buffalocli@v0.0.0-20230801092127-b85bfd5d395a/internal/cmd/test/test.go (about)

     1  package test
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"github.com/gobuffalo/envy"
    13  	"github.com/gobuffalo/meta"
    14  	"github.com/gobuffalo/pop/v6"
    15  	"github.com/sirupsen/logrus"
    16  	"github.com/spf13/cobra"
    17  )
    18  
    19  func runE(c *cobra.Command, args []string) error {
    20  	os.Setenv("GO_ENV", "test")
    21  	if _, err := os.Stat("database.yml"); err == nil {
    22  		// there's a database
    23  		test, err := pop.Connect("test")
    24  		if err != nil {
    25  			return err
    26  		}
    27  
    28  		// drop the test db:
    29  		if err := test.Dialect.DropDB(); err != nil {
    30  			// not an error, since the database will be created in the next step anyway
    31  			logrus.Info("no test database to drop")
    32  		}
    33  
    34  		// create the test db:
    35  		err = test.Dialect.CreateDB()
    36  		if err != nil {
    37  			return err
    38  		}
    39  
    40  		// Read and remove --force-migrations flag from args:
    41  		forceMigrations := strings.Contains(strings.Join(args, ""), "--force-migrations")
    42  		args = cutArg("--force-migrations", args)
    43  		if forceMigrations {
    44  			fm, err := pop.NewFileMigrator("./migrations", test)
    45  			if err != nil {
    46  				return err
    47  			}
    48  
    49  			if err := fm.Up(); err != nil {
    50  				return err
    51  			}
    52  
    53  			return testRunner(args)
    54  		}
    55  
    56  		if schema := findSchema(); schema != nil {
    57  			err = test.Dialect.LoadSchema(schema)
    58  			if err != nil {
    59  				return err
    60  			}
    61  		}
    62  	}
    63  	return testRunner(args)
    64  }
    65  
    66  func findSchema() io.Reader {
    67  	if f, err := os.Open(filepath.Join("migrations", "schema.sql")); err == nil {
    68  		return f
    69  	}
    70  	if dev, err := pop.Connect("development"); err == nil {
    71  		schema := &bytes.Buffer{}
    72  		if err = dev.Dialect.DumpSchema(schema); err == nil {
    73  			return schema
    74  		}
    75  	}
    76  
    77  	if test, err := pop.Connect("test"); err == nil {
    78  		fm, err := pop.NewFileMigrator("./migrations", test)
    79  		if err != nil {
    80  			return nil
    81  		}
    82  
    83  		if err := fm.Up(); err == nil {
    84  			return nil
    85  		}
    86  	}
    87  	return nil
    88  }
    89  
    90  func testRunner(args []string) error {
    91  	var mFlag bool
    92  	var query string
    93  
    94  	commandArgs := []string{}
    95  	packageArgs := []string{}
    96  
    97  	var lastArg string
    98  	for index, arg := range args {
    99  		switch arg {
   100  		case "-run", "-m":
   101  			query = args[index+1]
   102  			mFlag = true
   103  		case "-v", "-timeout":
   104  			commandArgs = append(commandArgs, arg)
   105  		default:
   106  			if lastArg == "-timeout" {
   107  				commandArgs = append(commandArgs, arg)
   108  			} else if lastArg != "-run" && lastArg != "-m" {
   109  				packageArgs = append(packageArgs, arg)
   110  			}
   111  		}
   112  
   113  		lastArg = arg
   114  	}
   115  
   116  	cmd := newTestCmd(commandArgs)
   117  	if mFlag {
   118  		return mFlagRunner{
   119  			query: query,
   120  			args:  commandArgs,
   121  			pargs: packageArgs,
   122  		}.Run()
   123  	}
   124  
   125  	pkgs, err := testPackages(packageArgs)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	cmd.Args = append(cmd.Args, pkgs...)
   131  	logrus.Info(strings.Join(cmd.Args, " "))
   132  	return cmd.Run()
   133  }
   134  
   135  type mFlagRunner struct {
   136  	query string
   137  	args  []string
   138  	pargs []string
   139  }
   140  
   141  func (m mFlagRunner) Run() error {
   142  	app := meta.New(".")
   143  	pwd, _ := os.Getwd()
   144  	defer os.Chdir(pwd)
   145  
   146  	pkgs, err := testPackages(m.pargs)
   147  	if err != nil {
   148  		return err
   149  	}
   150  
   151  	var errs bool
   152  	for _, p := range pkgs {
   153  		os.Chdir(pwd)
   154  
   155  		if p == app.PackagePkg {
   156  			continue
   157  		}
   158  
   159  		cmd := newTestCmd(m.args)
   160  
   161  		p = strings.TrimPrefix(p, app.PackagePkg+string(filepath.Separator))
   162  		os.Chdir(p)
   163  
   164  		if hasTestify(cmd.Args) {
   165  			cmd.Args = append(cmd.Args, "-testify.m", m.query)
   166  		} else {
   167  			cmd.Args = append(cmd.Args, "-run", m.query)
   168  		}
   169  
   170  		logrus.Info(strings.Join(cmd.Args, " "))
   171  
   172  		if err := cmd.Run(); err != nil {
   173  			errs = true
   174  		}
   175  	}
   176  	if errs {
   177  		return fmt.Errorf("errors running tests")
   178  	}
   179  	return nil
   180  }
   181  
   182  func hasTestify(args []string) bool {
   183  	cmd := exec.Command(args[0], args[1:]...)
   184  	cmd.Args = append(cmd.Args, "-unknownflag")
   185  	b, _ := cmd.Output()
   186  	return bytes.Contains(b, []byte("-testify.m"))
   187  }
   188  
   189  func testPackages(givenArgs []string) ([]string, error) {
   190  	// If there are args, then assume these are the packages to test.
   191  	//
   192  	// Instead of always returning all packages from 'go list ./...', just
   193  	// return the given packages in this case
   194  	if len(givenArgs) > 0 {
   195  		return givenArgs, nil
   196  	}
   197  
   198  	args := []string{}
   199  	out, err := exec.Command(envy.Get("GO_BIN", "go"), "list", "./...").Output()
   200  	if err != nil {
   201  		return args, err
   202  	}
   203  	pkgs := bytes.Split(bytes.TrimSpace(out), []byte("\n"))
   204  	for _, p := range pkgs {
   205  		if !strings.Contains(string(p), "/vendor/") {
   206  			args = append(args, string(p))
   207  		}
   208  	}
   209  	return args, nil
   210  }
   211  
   212  func newTestCmd(args []string) *exec.Cmd {
   213  	cargs := []string{"test", "-p", "1"}
   214  	app := meta.New(".")
   215  	cargs = append(cargs, "-tags", app.BuildTags("development").String())
   216  	cargs = append(cargs, args...)
   217  	cmd := exec.Command(envy.Get("GO_BIN", "go"), cargs...)
   218  	cmd.Stdin = os.Stdin
   219  	cmd.Stdout = os.Stdout
   220  	cmd.Stderr = os.Stderr
   221  	return cmd
   222  }
   223  
   224  func cutArg(arg string, args []string) []string {
   225  	for i, v := range args {
   226  		if v == arg {
   227  			return append(args[:i], args[i+1:]...)
   228  		}
   229  	}
   230  
   231  	return args
   232  }