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  }