github.com/zaquestion/lab@v0.25.1/cmd/root_test.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "io" 6 "math/rand" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "strconv" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/acarl005/stripansi" 16 "github.com/otiai10/copy" 17 "github.com/spf13/viper" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 gitlab "github.com/xanzy/go-gitlab" 21 lab "github.com/zaquestion/lab/internal/gitlab" 22 ) 23 24 var labBinaryPath string 25 26 func TestMain(m *testing.M) { 27 rand.Seed(time.Now().UnixNano()) 28 // Build a lab binary with test symbols. If the parent test binary was run 29 // with coverage enabled, enable coverage on the child binary, too. 30 var err error 31 labBinaryPath, err = filepath.Abs(os.ExpandEnv("$GOPATH/src/github.com/zaquestion/lab/testdata/" + labBinary)) 32 if err != nil { 33 log.Fatal(err) 34 } 35 testCmd := []string{"test", "-c", "-o", labBinaryPath, "github.com/zaquestion/lab"} 36 if coverMode := testing.CoverMode(); coverMode != "" { 37 testCmd = append(testCmd, "-covermode", coverMode, "-coverpkg", "./...") 38 } 39 if out, err := exec.Command("go", testCmd...).CombinedOutput(); err != nil { 40 log.Fatalf("Error building lab test binary: %s (%s)", string(out), err) 41 } 42 43 originalWd, err := os.Getwd() 44 if err != nil { 45 log.Fatal(err) 46 } 47 // Make a copy of the testdata Git test project and chdir to it. 48 repo := copyTestRepo(log) 49 if err := os.Chdir(repo); err != nil { 50 log.Fatalf("Error chdir to testdata: %s", err) 51 } 52 // Load config for non-testbinary based tests 53 viper.SetConfigName("lab") 54 viper.SetConfigType("toml") 55 viper.AddConfigPath(".") 56 err = viper.ReadInConfig() 57 if err != nil { 58 log.Fatal(err) 59 } 60 host := viper.GetString("core.host") 61 token := viper.GetString("core.token") 62 63 client, _ := gitlab.NewClient(token, gitlab.WithBaseURL(host+"/api/v4")) 64 u, _, err := client.Users.CurrentUser() 65 if err != nil { 66 log.Fatal(err) 67 } 68 lab.Init(host, u.Username, token, false) 69 70 // Make "origin" the default remote for test cases calling 71 // cmd.Run() directly, instead of launching the labBinaryPath 72 // for getting these vars correctly set through Execute(). 73 defaultRemote = "origin" 74 forkRemote = "origin" 75 code := m.Run() 76 77 if err := os.Chdir(originalWd); err != nil { 78 log.Fatalf("Error chdir to original working dir: %s", err) 79 } 80 os.Remove(labBinaryPath) 81 testdirs, err := filepath.Glob(os.ExpandEnv("$GOPATH/src/github.com/zaquestion/lab/testdata-*")) 82 if err != nil { 83 log.Infof("Error listing glob testdata-*: %s", err) 84 } 85 for _, dir := range testdirs { 86 err := os.RemoveAll(dir) 87 if err != nil { 88 log.Infof("Error removing dir %s: %s", dir, err) 89 } 90 } 91 92 os.Exit(code) 93 } 94 95 func TestRootCloneNoArg(t *testing.T) { 96 cmd := exec.Command(labBinaryPath, "clone") 97 b, _ := cmd.CombinedOutput() 98 require.Contains(t, string(b), "You must specify a repository to clone.") 99 } 100 101 func TestRootNoArg(t *testing.T) { 102 cmd := exec.Command(labBinaryPath) 103 b, err := cmd.CombinedOutput() 104 if err != nil { 105 t.Log(string(b)) 106 t.Fatal(err) 107 } 108 assert.Contains(t, string(b), `lab: A GitLab Command Line Interface Utility 109 110 Usage: 111 lab [flags] 112 lab [command]`) 113 } 114 115 func TestRootHelp(t *testing.T) { 116 cmd := exec.Command(labBinaryPath, "help") 117 b, err := cmd.CombinedOutput() 118 if err != nil { 119 t.Log(string(b)) 120 t.Fatal(err) 121 } 122 res := string(b) 123 assert.Contains(t, res, `Show the help for lab 124 125 Usage: 126 lab help [command [subcommand...]] [flags]`) 127 } 128 129 type fatalLogger interface { 130 Fatal(...interface{}) 131 } 132 133 // copyTestRepo creates a copy of the testdata directory (contains a Git repo) in 134 // the project root with a random dir name. It returns the absolute path of the 135 // new testdata dir. 136 // Note: testdata-* must be in the .gitignore or the copies will create write 137 // errors as Git attempts to add the Git repo to the the project repo's index. 138 func copyTestRepo(log fatalLogger) string { 139 dstDir := strconv.FormatUint(rand.Uint64(), 10) 140 dst, err := filepath.Abs( 141 os.ExpandEnv("$GOPATH/src/github.com/zaquestion/lab/testdata-" + dstDir)) 142 if err != nil { 143 log.Fatal(err) 144 } 145 src, err := filepath.Abs( 146 os.ExpandEnv("$GOPATH/src/github.com/zaquestion/lab/testdata")) 147 if err != nil { 148 log.Fatal(err) 149 } 150 if err := copy.Copy(src, dst); err != nil { 151 log.Fatal(err) 152 } 153 // Move the test.git dir into the expected path at .git 154 if err := copy.Copy(dst+"/test.git", dst+"/.git"); err != nil { 155 log.Fatal(err) 156 } 157 return dst 158 } 159 160 func whoAmI() string { 161 cmd := exec.Command("whoami") 162 whoami, err := cmd.CombinedOutput() 163 if err != nil { 164 log.Fatal(err) 165 } 166 return string(whoami) 167 } 168 169 func configFile() string { 170 str := "/home/" + whoAmI() + "/.config/lab" 171 str = strings.Replace(str, "\n", "", -1) 172 if _, err := os.Stat(str); os.IsNotExist(err) { 173 os.MkdirAll(str, os.ModePerm) 174 } 175 str = str + "/lab.toml" 176 return str 177 } 178 179 // getAppOutput splits and truncates the list of strings returned from the "lab" 180 // test binary to remove the test-specific output. It use "PASS" as a marker for 181 // the end of the app output and the beginning of the test output. 182 func getAppOutput(output []byte) []string { 183 lines := strings.Split(string(output), "\n") 184 for i, line := range lines { 185 if line == "PASS" { 186 return lines[:i] 187 } 188 } 189 return lines 190 } 191 192 func setConfigValues(repo string, configVal string, gitVal string) error { 193 err := copy.Copy(repo+"/lab.toml", configFile()) 194 if err != nil { 195 log.Errorln(err) 196 return err 197 } 198 199 configfile, err := os.OpenFile(configFile(), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 200 if err != nil { 201 log.Fatal(err) 202 } 203 204 if _, err := configfile.WriteString("\n[mr_show]\n comments = " + configVal + "\n"); err != nil { 205 log.Fatal(err) 206 } 207 configfile.Close() 208 209 err = os.Mkdir(repo+"/.git/lab/", 0700) 210 if err != nil { 211 log.Fatal(err) 212 } 213 gitfile, err := os.OpenFile(repo+"/.git/lab/lab.toml", os.O_CREATE|os.O_WRONLY, 0644) 214 if err != nil { 215 log.Fatal(err) 216 } 217 218 if _, err = gitfile.WriteString("\n[mr_show]\n comments = " + gitVal + "\n"); err != nil { 219 log.Fatal(err) 220 } 221 gitfile.Close() 222 223 return nil 224 } 225 226 // There isn't a really good way to test the config override 227 // infrastruture, so just call 'mr show' and set 'mr_show.comments' 228 func Test_config_gitConfig_FF(t *testing.T) { 229 repo := copyTestRepo(t) 230 231 err := setConfigValues(repo, "false", "false") 232 if err != nil { 233 t.Skip(err) 234 } 235 os.Remove(repo + "/lab.toml") 236 237 cmd := exec.Command(labBinaryPath, "mr", "show", "1") 238 cmd.Dir = repo 239 240 b, err := cmd.CombinedOutput() 241 if err != nil { 242 t.Log(string(b)) 243 t.Error(err) 244 } 245 out := stripansi.Strip(string(b)) 246 247 os.Remove(configFile()) 248 // both configs set to false, comments should not be output 249 require.NotContains(t, string(out), `commented at`) 250 } 251 252 func Test_config_gitConfig_FT(t *testing.T) { 253 repo := copyTestRepo(t) 254 255 err := setConfigValues(repo, "false", "true") 256 if err != nil { 257 t.Skip(err) 258 } 259 os.Remove(repo + "/lab.toml") 260 261 cmd := exec.Command(labBinaryPath, "mr", "show", "1") 262 cmd.Dir = repo 263 264 b, err := cmd.CombinedOutput() 265 if err != nil { 266 t.Log(string(b)) 267 t.Error(err) 268 } 269 out := stripansi.Strip(string(b)) 270 271 os.Remove(configFile()) 272 // .config set to false and .git set to true, comments should be 273 // output 274 require.Contains(t, string(out), `commented at`) 275 } 276 277 func Test_config_gitConfig_TF(t *testing.T) { 278 repo := copyTestRepo(t) 279 280 err := setConfigValues(repo, "true", "false") 281 if err != nil { 282 t.Skip(err) 283 } 284 os.Remove(repo + "/lab.toml") 285 286 cmd := exec.Command(labBinaryPath, "mr", "show", "1") 287 cmd.Dir = repo 288 289 b, err := cmd.CombinedOutput() 290 if err != nil { 291 t.Log(string(b)) 292 t.Error(err) 293 } 294 out := stripansi.Strip(string(b)) 295 296 os.Remove(configFile()) 297 // .config set to true and .git set to false, comments should not be 298 // output 299 require.NotContains(t, string(out), `commented at`) 300 } 301 302 func Test_config_gitConfig_TT(t *testing.T) { 303 repo := copyTestRepo(t) 304 305 err := setConfigValues(repo, "true", "true") 306 if err != nil { 307 t.Skip(err) 308 } 309 os.Remove(repo + "/lab.toml") 310 311 cmd := exec.Command(labBinaryPath, "mr", "show", "1") 312 cmd.Dir = repo 313 314 b, err := cmd.CombinedOutput() 315 if err != nil { 316 t.Log(string(b)) 317 t.Error(err) 318 } 319 out := stripansi.Strip(string(b)) 320 321 os.Remove(configFile()) 322 // both configs set to true, comments should be output 323 require.Contains(t, string(out), `commented at`) 324 } 325 326 // Some flag and config tests do not have to be run. 327 // flag not set, config true == comments 328 // This case is handled by Test_config_gitConfig_TT 329 // flag not set, config false == no comments 330 // This case is handled by Test_config_gitConfig_FF 331 // flag not set, config not set == no comments 332 // flag set, config not set == comments 333 // These case are handled in cmd/mr_show_test.go 334 335 // flag set, config true == comments 336 func Test_flag_config_TT(t *testing.T) { 337 repo := copyTestRepo(t) 338 339 err := setConfigValues(repo, "true", "true") 340 if err != nil { 341 t.Skip(err) 342 } 343 os.Remove(repo + "/lab.toml") 344 345 cmd := exec.Command(labBinaryPath, "mr", "show", "1", "--comments") 346 cmd.Dir = repo 347 348 b, err := cmd.CombinedOutput() 349 if err != nil { 350 t.Log(string(b)) 351 t.Error(err) 352 } 353 out := stripansi.Strip(string(b)) 354 355 os.Remove(configFile()) 356 // both configs set to true, comments should be output 357 require.Contains(t, string(out), `commented at`) 358 } 359 360 // flag set, config false == comments 361 func Test_flag_config_TF(t *testing.T) { 362 repo := copyTestRepo(t) 363 364 err := setConfigValues(repo, "false", "false") 365 if err != nil { 366 t.Skip(err) 367 } 368 os.Remove(repo + "/lab.toml") 369 370 cmd := exec.Command(labBinaryPath, "mr", "show", "1", "--comments") 371 cmd.Dir = repo 372 373 b, err := cmd.CombinedOutput() 374 if err != nil { 375 t.Log(string(b)) 376 t.Error(err) 377 } 378 out := stripansi.Strip(string(b)) 379 380 os.Remove(configFile()) 381 // both configs set to true, comments should be output 382 require.Contains(t, string(out), `commented at`) 383 } 384 385 // flag (explicitly) unset, config true == no comments 386 func Test_flag_config_FT(t *testing.T) { 387 repo := copyTestRepo(t) 388 389 err := setConfigValues(repo, "true", "true") 390 if err != nil { 391 t.Skip(err) 392 } 393 os.Remove(repo + "/lab.toml") 394 395 cmd := exec.Command(labBinaryPath, "mr", "show", "1", "--comments=false") 396 cmd.Dir = repo 397 398 b, err := cmd.CombinedOutput() 399 if err != nil { 400 t.Log(string(b)) 401 t.Error(err) 402 } 403 out := stripansi.Strip(string(b)) 404 405 os.Remove(configFile()) 406 // configs overridden on the command line, comments should not be output 407 require.NotContains(t, string(out), `commented at`) 408 } 409 410 // Make sure the version command don't break things in the future 411 func Test_versionCmd(t *testing.T) { 412 413 t.Run("version", func(t *testing.T) { 414 labCmd := exec.Command(labBinaryPath, "version") 415 out, err := labCmd.CombinedOutput() 416 if err != nil { 417 t.Log(string(out)) 418 t.Fatal(err) 419 } 420 assert.Contains(t, string(out), "lab version "+Version) 421 }) 422 t.Run("--version", func(t *testing.T) { 423 old := os.Stdout // keep backup of the real stdout 424 r, w, _ := os.Pipe() 425 os.Stdout = w 426 427 RootCmd.Flag("version").Value.Set("true") 428 RootCmd.Run(RootCmd, nil) 429 430 outC := make(chan string) 431 // copy the output in a separate goroutine so printing can't block indefinitely 432 go func() { 433 var buf bytes.Buffer 434 io.Copy(&buf, r) 435 outC <- buf.String() 436 }() 437 438 // back to normal state 439 w.Close() 440 os.Stdout = old // restoring the real stdout 441 out := <-outC 442 443 assert.Contains(t, out, "lab version "+Version) 444 }) 445 }