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 }