github.com/FollowTheProcess/tag@v0.4.2/app/app_test.go (about)

     1  package app
     2  
     3  import (
     4  	"bytes"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/FollowTheProcess/tag/config"
    13  	"github.com/FollowTheProcess/tag/git"
    14  )
    15  
    16  // setup creates a tempdir, initialises a git repo with a README to do
    17  // search and replace on, adds an initial tag v0.1.0 as well as a tag config
    18  // returns the path to the root of the test repo as well as a teardown
    19  // function that removes the entire directory at the end of the test.
    20  // Usage in a test would be:
    21  //
    22  //	tmp, teardown := setup(t)
    23  //	defer teardown()
    24  func setup(t *testing.T) (string, func()) {
    25  	t.Helper()
    26  	tmp, err := os.MkdirTemp("", "test")
    27  	if err != nil {
    28  		t.Fatalf("Could not create temp dir: %v", err)
    29  	}
    30  	err = os.WriteFile(filepath.Join(tmp, "README.md"), []byte("Hello, version 0.1.0"), 0o755)
    31  	if err != nil {
    32  		t.Fatalf("Could not create README.md: %v", err)
    33  	}
    34  
    35  	cfg := []byte(`
    36  	version = '0.1.0'
    37  
    38  	[git]
    39  	default-branch = 'main'
    40  	message-template = 'Bump version {{.Current}} -> {{.Next}}'
    41  	tag-template = 'v{{.Next}}'
    42  	
    43  	[hooks]
    44  	pre-replace = "echo 'I run before doing anything'"
    45  	pre-commit = "echo 'I run after replacing but before committing changes'"
    46  	pre-tag = "echo 'I run after committing changes but before tagging'"
    47  	pre-push = "echo 'I run after tagging, but before pushing'"
    48  
    49  	[[file]]
    50  	path = 'README.md'
    51  	search = 'Hello, version {{.Current}}'	
    52  	`)
    53  
    54  	err = os.WriteFile(filepath.Join(tmp, ".tag.toml"), cfg, 0o755)
    55  	if err != nil {
    56  		t.Fatalf("Could not create .tag.toml: %v", err)
    57  	}
    58  
    59  	gitConfigEmail := exec.Command("git", "config", "--local", "user.email", "tagtest@gmail.com")
    60  	gitConfigEmail.Dir = tmp
    61  
    62  	gitConfigName := exec.Command("git", "config", "--local", "user.name", "Tag Test")
    63  	gitConfigName.Dir = tmp
    64  
    65  	init := exec.Command("git", "init", "--initial-branch=main")
    66  	init.Dir = tmp
    67  
    68  	add := exec.Command("git", "add", "-A")
    69  	add.Dir = tmp
    70  
    71  	commit := exec.Command("git", "commit", "-m", "test commit")
    72  	commit.Dir = tmp
    73  
    74  	firstTag := exec.Command("git", "tag", "-a", "v0.1.0", "-m", "test tag")
    75  	firstTag.Dir = tmp
    76  
    77  	err = init.Run()
    78  	if err != nil {
    79  		t.Fatalf("Error initialising test git repo: %v", err)
    80  	}
    81  
    82  	stdout, err := add.CombinedOutput()
    83  	if err != nil {
    84  		t.Fatalf("Error adding files to test git repo: %s", string(stdout))
    85  	}
    86  
    87  	stdout, err = gitConfigEmail.CombinedOutput()
    88  	if err != nil {
    89  		t.Fatalf("git config user.email returned an error: %s", string(stdout))
    90  	}
    91  
    92  	stdout, err = gitConfigName.CombinedOutput()
    93  	if err != nil {
    94  		t.Fatalf("git config user.name returned an error: %s", string(stdout))
    95  	}
    96  
    97  	stdout, err = commit.CombinedOutput()
    98  	if err != nil {
    99  		t.Fatalf("Error committing to the test git repo: %s", string(stdout))
   100  	}
   101  
   102  	stdout, err = firstTag.CombinedOutput()
   103  	if err != nil {
   104  		t.Fatalf("Error issuing the first tag to test git repo: %s", string(stdout))
   105  	}
   106  
   107  	tearDown := func() { os.RemoveAll(tmp) }
   108  
   109  	return tmp, tearDown
   110  }
   111  
   112  // newTestApp creates an app set up for testing.
   113  func newTestApp(out io.Writer) App {
   114  	app := App{
   115  		Stdout: out,
   116  		Cfg: config.Config{
   117  			Version: "0.1.0",
   118  			Files: []config.File{
   119  				{
   120  					Path:   "README.md",
   121  					Search: "version = {{.Current}}",
   122  				},
   123  			},
   124  		},
   125  		replaceMode: true,
   126  	}
   127  	return app
   128  }
   129  
   130  func TestAppLatest(t *testing.T) {
   131  	tmp, teardown := setup(t)
   132  	defer teardown()
   133  
   134  	err := os.Chdir(tmp)
   135  	if err != nil {
   136  		t.Fatalf("Could not change dir to tmp: %v", err)
   137  	}
   138  	out := &bytes.Buffer{}
   139  	app := newTestApp(out)
   140  
   141  	err = app.Latest()
   142  	if err != nil {
   143  		t.Fatalf("app.Latest returned an error: %v", err)
   144  	}
   145  
   146  	if out.String() != "v0.1.0\n" {
   147  		t.Errorf("app.Latest incorrect stdout: got %q, wanted %q", out.String(), "v0.1.0\n")
   148  	}
   149  }
   150  
   151  func TestAppList(t *testing.T) {
   152  	tmp, teardown := setup(t)
   153  	defer teardown()
   154  
   155  	err := os.Chdir(tmp)
   156  	if err != nil {
   157  		t.Fatalf("Could not change dir to tmp: %v", err)
   158  	}
   159  	out := &bytes.Buffer{}
   160  	app := newTestApp(out)
   161  
   162  	err = app.List(10)
   163  	if err != nil {
   164  		t.Fatalf("app.List returned an error: %v", err)
   165  	}
   166  
   167  	if out.String() != "v0.1.0\n" {
   168  		t.Errorf("app.List incorrect stdout: got %q, wanted %q", out.String(), "v0.1.0\n")
   169  	}
   170  }
   171  
   172  func TestAppMajor(t *testing.T) {
   173  	tmp, teardown := setup(t)
   174  	defer teardown()
   175  
   176  	err := os.Chdir(tmp)
   177  	if err != nil {
   178  		t.Fatalf("Could not change dir to tmp: %v", err)
   179  	}
   180  	appOut := &bytes.Buffer{}
   181  	appErr := &bytes.Buffer{}
   182  	app, err := New(tmp, appOut, appErr)
   183  	if err != nil {
   184  		t.Fatalf("app.New returned an error: %v", err)
   185  	}
   186  
   187  	err = app.Major(false, true, false)
   188  	if err != nil {
   189  		t.Fatalf("app.Major returned an error: %v", err)
   190  	}
   191  
   192  	// Check that it's replaced the README contents
   193  	readme, err := os.ReadFile("README.md")
   194  	if err != nil {
   195  		t.Fatalf("Could not read from replaced README: %v", err)
   196  	}
   197  	want := "Hello, version 1.0.0"
   198  
   199  	if string(readme) != want {
   200  		t.Errorf("README replaced incorrectly: got %q, wanted %q", string(readme), want)
   201  	}
   202  
   203  	// Check it's replaced the config version but not the message templates etc.
   204  	cfg, err := config.Load(filepath.Join(tmp, ".tag.toml"))
   205  	if err != nil {
   206  		t.Fatalf("Could not read replaced config file: %v", err)
   207  	}
   208  
   209  	if cfg.Version != "1.0.0" {
   210  		t.Errorf("Wrong version in replaced config file. Got %s, wanted %s", cfg.Version, "1.0.0")
   211  	}
   212  
   213  	if cfg.Git.MessageTemplate != "Bump version {{.Current}} -> {{.Next}}" {
   214  		t.Errorf("Wrong message template in replaced config file. Got %s, wanted %s", cfg.Git.MessageTemplate, "Bump version {{.Current}} -> {{.Next}}")
   215  	}
   216  
   217  	if cfg.Git.TagTemplate != "v{{.Next}}" {
   218  		t.Errorf("Wrong tag template in replaced config file. Got %s, wanted %s", cfg.Git.TagTemplate, "v{{.Next}}")
   219  	}
   220  
   221  	// Check it's made the appropriate commit
   222  	gitLog := exec.Command("git", "log", "--oneline")
   223  	stdout, err := gitLog.CombinedOutput()
   224  	if err != nil {
   225  		t.Fatalf("Error getting git log: %s", string(stdout))
   226  	}
   227  
   228  	if !strings.Contains(string(stdout), "Bump version 0.1.0 -> 1.0.0") {
   229  		t.Errorf("Expected bump version commit not found in git log: %s", string(stdout))
   230  	}
   231  
   232  	// Check the working tree is clean
   233  	dirty, err := git.IsDirty()
   234  	if err != nil {
   235  		t.Fatalf("git.IsDirty returned an error: %v", err)
   236  	}
   237  	if dirty {
   238  		t.Error("Working tree was left dirty after replacing files")
   239  	}
   240  
   241  	// Check the latest tag is correct
   242  	latest, err := git.LatestTag()
   243  	if err != nil {
   244  		t.Errorf("Could not get latest tag: %v", err)
   245  	}
   246  	if latest != "v1.0.0" {
   247  		t.Errorf("Wrong latest tag: got %s, wanted %s", latest, "v1.0.0")
   248  	}
   249  }
   250  
   251  func TestAppMajorDryRun(t *testing.T) {
   252  	tmp, teardown := setup(t)
   253  	defer teardown()
   254  
   255  	err := os.Chdir(tmp)
   256  	if err != nil {
   257  		t.Fatalf("Could not change dir to tmp: %v", err)
   258  	}
   259  	appOut := &bytes.Buffer{}
   260  	appErr := &bytes.Buffer{}
   261  	app, err := New(tmp, appOut, appErr)
   262  	if err != nil {
   263  		t.Fatalf("app.New returned an error: %v", err)
   264  	}
   265  
   266  	err = app.Major(false, true, true)
   267  	if err != nil {
   268  		t.Fatalf("app.Major returned an error: %v", err)
   269  	}
   270  
   271  	// Check that it's not replaced the README contents
   272  	readme, err := os.ReadFile("README.md")
   273  	if err != nil {
   274  		t.Fatalf("Could not read from replaced README: %v", err)
   275  	}
   276  	want := "Hello, version 0.1.0"
   277  
   278  	if string(readme) != want {
   279  		t.Errorf("README replaced despite dry-run: got %q, wanted %q", string(readme), want)
   280  	}
   281  
   282  	// Check it's not made a commit
   283  	gitLog := exec.Command("git", "log", "--oneline")
   284  	stdout, err := gitLog.CombinedOutput()
   285  	if err != nil {
   286  		t.Fatalf("Error getting git log: %s", string(stdout))
   287  	}
   288  
   289  	if !strings.Contains(string(stdout), "test commit") {
   290  		t.Errorf("Made a commit despite dry-run: %s", string(stdout))
   291  	}
   292  
   293  	// Check the working tree is clean
   294  	dirty, err := git.IsDirty()
   295  	if err != nil {
   296  		t.Fatalf("git.IsDirty returned an error: %v", err)
   297  	}
   298  	if dirty {
   299  		t.Error("Working tree is dirty after dry-run")
   300  	}
   301  
   302  	// Check the latest tag is correct
   303  	latest, err := git.LatestTag()
   304  	if err != nil {
   305  		t.Errorf("Could not get latest tag: %v", err)
   306  	}
   307  	if latest != "v0.1.0" {
   308  		t.Errorf("Wrong latest tag: got %s, wanted %s", latest, "v0.1.0")
   309  	}
   310  }
   311  
   312  func TestAppMinor(t *testing.T) {
   313  	tmp, teardown := setup(t)
   314  	defer teardown()
   315  
   316  	err := os.Chdir(tmp)
   317  	if err != nil {
   318  		t.Fatalf("Could not change dir to tmp: %v", err)
   319  	}
   320  	appOut := &bytes.Buffer{}
   321  	appErr := &bytes.Buffer{}
   322  	app, err := New(tmp, appOut, appErr)
   323  	if err != nil {
   324  		t.Fatalf("app.New returned an error: %v", err)
   325  	}
   326  
   327  	err = app.Minor(false, true, false)
   328  	if err != nil {
   329  		t.Fatalf("app.Minor returned an error: %v", err)
   330  	}
   331  
   332  	// Check that it's replaced the README contents
   333  	readme, err := os.ReadFile("README.md")
   334  	if err != nil {
   335  		t.Fatalf("Could not read from replaced README: %v", err)
   336  	}
   337  	want := "Hello, version 0.2.0"
   338  
   339  	if string(readme) != want {
   340  		t.Errorf("README replaced incorrectly: got %q, wanted %q", string(readme), want)
   341  	}
   342  
   343  	// Check it's replaced the config version but not the message templates etc.
   344  	cfg, err := config.Load(filepath.Join(tmp, ".tag.toml"))
   345  	if err != nil {
   346  		t.Fatalf("Could not read replaced config file: %v", err)
   347  	}
   348  
   349  	if cfg.Version != "0.2.0" {
   350  		t.Errorf("Wrong version in replaced config file. Got %s, wanted %s", cfg.Version, "0.2.0")
   351  	}
   352  
   353  	if cfg.Git.MessageTemplate != "Bump version {{.Current}} -> {{.Next}}" {
   354  		t.Errorf("Wrong message template in replaced config file. Got %s, wanted %s", cfg.Git.MessageTemplate, "Bump version {{.Current}} -> {{.Next}}")
   355  	}
   356  
   357  	if cfg.Git.TagTemplate != "v{{.Next}}" {
   358  		t.Errorf("Wrong tag template in replaced config file. Got %s, wanted %s", cfg.Git.TagTemplate, "v{{.Next}}")
   359  	}
   360  
   361  	// Check it's made the appropriate commit
   362  	gitLog := exec.Command("git", "log", "--oneline")
   363  	stdout, err := gitLog.CombinedOutput()
   364  	if err != nil {
   365  		t.Fatalf("Error getting git log: %s", string(stdout))
   366  	}
   367  
   368  	if !strings.Contains(string(stdout), "Bump version 0.1.0 -> 0.2.0") {
   369  		t.Errorf("Expected bump version commit not found in git log: %s", string(stdout))
   370  	}
   371  
   372  	// Check the working tree is clean
   373  	dirty, err := git.IsDirty()
   374  	if err != nil {
   375  		t.Fatalf("git.IsDirty returned an error: %v", err)
   376  	}
   377  	if dirty {
   378  		t.Error("Working tree was left dirty after replacing files")
   379  	}
   380  
   381  	// Check the latest tag is correct
   382  	latest, err := git.LatestTag()
   383  	if err != nil {
   384  		t.Errorf("Could not get latest tag: %v", err)
   385  	}
   386  	if latest != "v0.2.0" {
   387  		t.Errorf("Wrong latest tag: got %s, wanted %s", latest, "v0.2.0")
   388  	}
   389  }
   390  
   391  func TestAppMinorDryRun(t *testing.T) {
   392  	tmp, teardown := setup(t)
   393  	defer teardown()
   394  
   395  	err := os.Chdir(tmp)
   396  	if err != nil {
   397  		t.Fatalf("Could not change dir to tmp: %v", err)
   398  	}
   399  	appOut := &bytes.Buffer{}
   400  	appErr := &bytes.Buffer{}
   401  	app, err := New(tmp, appOut, appErr)
   402  	if err != nil {
   403  		t.Fatalf("app.New returned an error: %v", err)
   404  	}
   405  
   406  	err = app.Minor(false, true, true)
   407  	if err != nil {
   408  		t.Fatalf("app.Minor returned an error: %v", err)
   409  	}
   410  
   411  	// Check that it's not replaced the README contents
   412  	readme, err := os.ReadFile("README.md")
   413  	if err != nil {
   414  		t.Fatalf("Could not read from replaced README: %v", err)
   415  	}
   416  	want := "Hello, version 0.1.0"
   417  
   418  	if string(readme) != want {
   419  		t.Errorf("README replaced despite dry-run: got %q, wanted %q", string(readme), want)
   420  	}
   421  
   422  	// Check it's not made a commit
   423  	gitLog := exec.Command("git", "log", "--oneline")
   424  	stdout, err := gitLog.CombinedOutput()
   425  	if err != nil {
   426  		t.Fatalf("Error getting git log: %s", string(stdout))
   427  	}
   428  
   429  	if !strings.Contains(string(stdout), "test commit") {
   430  		t.Errorf("Made a commit despite dry-run: %s", string(stdout))
   431  	}
   432  
   433  	// Check the working tree is clean
   434  	dirty, err := git.IsDirty()
   435  	if err != nil {
   436  		t.Fatalf("git.IsDirty returned an error: %v", err)
   437  	}
   438  	if dirty {
   439  		t.Error("Working tree is dirty after dry-run")
   440  	}
   441  
   442  	// Check the latest tag is correct
   443  	latest, err := git.LatestTag()
   444  	if err != nil {
   445  		t.Errorf("Could not get latest tag: %v", err)
   446  	}
   447  	if latest != "v0.1.0" {
   448  		t.Errorf("Wrong latest tag: got %s, wanted %s", latest, "v0.1.0")
   449  	}
   450  }
   451  
   452  func TestAppPatch(t *testing.T) {
   453  	tmp, teardown := setup(t)
   454  	defer teardown()
   455  
   456  	err := os.Chdir(tmp)
   457  	if err != nil {
   458  		t.Fatalf("Could not change dir to tmp: %v", err)
   459  	}
   460  	appOut := &bytes.Buffer{}
   461  	appErr := &bytes.Buffer{}
   462  	app, err := New(tmp, appOut, appErr)
   463  	if err != nil {
   464  		t.Fatalf("app.New returned an error: %v", err)
   465  	}
   466  
   467  	err = app.Patch(false, true, false)
   468  	if err != nil {
   469  		t.Fatalf("app.Patch returned an error: %v", err)
   470  	}
   471  
   472  	// Check that it's replaced the README contents
   473  	readme, err := os.ReadFile("README.md")
   474  	if err != nil {
   475  		t.Fatalf("Could not read from replaced README: %v", err)
   476  	}
   477  	want := "Hello, version 0.1.1"
   478  
   479  	if string(readme) != want {
   480  		t.Errorf("README replaced incorrectly: got %q, wanted %q", string(readme), want)
   481  	}
   482  
   483  	// Check it's replaced the config version but not the message templates etc.
   484  	cfg, err := config.Load(filepath.Join(tmp, ".tag.toml"))
   485  	if err != nil {
   486  		t.Fatalf("Could not read replaced config file: %v", err)
   487  	}
   488  
   489  	if cfg.Version != "0.1.1" {
   490  		t.Errorf("Wrong version in replaced config file. Got %s, wanted %s", cfg.Version, "0.1.1")
   491  	}
   492  
   493  	if cfg.Git.MessageTemplate != "Bump version {{.Current}} -> {{.Next}}" {
   494  		t.Errorf("Wrong message template in replaced config file. Got %s, wanted %s", cfg.Git.MessageTemplate, "Bump version {{.Current}} -> {{.Next}}")
   495  	}
   496  
   497  	if cfg.Git.TagTemplate != "v{{.Next}}" {
   498  		t.Errorf("Wrong tag template in replaced config file. Got %s, wanted %s", cfg.Git.TagTemplate, "v{{.Next}}")
   499  	}
   500  
   501  	// Check it's made the appropriate commit
   502  	gitLog := exec.Command("git", "log", "--oneline")
   503  	stdout, err := gitLog.CombinedOutput()
   504  	if err != nil {
   505  		t.Fatalf("Error getting git log: %s", string(stdout))
   506  	}
   507  
   508  	if !strings.Contains(string(stdout), "Bump version 0.1.0 -> 0.1.1") {
   509  		t.Errorf("Expected bump version commit not found in git log: %s", string(stdout))
   510  	}
   511  
   512  	// Check the working tree is clean
   513  	dirty, err := git.IsDirty()
   514  	if err != nil {
   515  		t.Fatalf("git.IsDirty returned an error: %v", err)
   516  	}
   517  	if dirty {
   518  		t.Error("Working tree was left dirty after replacing files")
   519  	}
   520  
   521  	// Check the latest tag is correct
   522  	latest, err := git.LatestTag()
   523  	if err != nil {
   524  		t.Errorf("Could not get latest tag: %v", err)
   525  	}
   526  	if latest != "v0.1.1" {
   527  		t.Errorf("Wrong latest tag: got %s, wanted %s", latest, "v0.1.1")
   528  	}
   529  }
   530  
   531  func TestAppPatchDryRun(t *testing.T) {
   532  	tmp, teardown := setup(t)
   533  	defer teardown()
   534  
   535  	err := os.Chdir(tmp)
   536  	if err != nil {
   537  		t.Fatalf("Could not change dir to tmp: %v", err)
   538  	}
   539  	appOut := &bytes.Buffer{}
   540  	appErr := &bytes.Buffer{}
   541  	app, err := New(tmp, appOut, appErr)
   542  	if err != nil {
   543  		t.Fatalf("app.New returned an error: %v", err)
   544  	}
   545  
   546  	err = app.Patch(false, true, true)
   547  	if err != nil {
   548  		t.Fatalf("app.Patch returned an error: %v", err)
   549  	}
   550  
   551  	// Check that it's not replaced the README contents
   552  	readme, err := os.ReadFile("README.md")
   553  	if err != nil {
   554  		t.Fatalf("Could not read from replaced README: %v", err)
   555  	}
   556  	want := "Hello, version 0.1.0"
   557  
   558  	if string(readme) != want {
   559  		t.Errorf("README replaced despite dry-run: got %q, wanted %q", string(readme), want)
   560  	}
   561  
   562  	// Check it's not made a commit
   563  	gitLog := exec.Command("git", "log", "--oneline")
   564  	stdout, err := gitLog.CombinedOutput()
   565  	if err != nil {
   566  		t.Fatalf("Error getting git log: %s", string(stdout))
   567  	}
   568  
   569  	if !strings.Contains(string(stdout), "test commit") {
   570  		t.Errorf("Made a commit despite dry-run: %s", string(stdout))
   571  	}
   572  
   573  	// Check the working tree is clean
   574  	dirty, err := git.IsDirty()
   575  	if err != nil {
   576  		t.Fatalf("git.IsDirty returned an error: %v", err)
   577  	}
   578  	if dirty {
   579  		t.Error("Working tree is dirty after dry-run")
   580  	}
   581  
   582  	// Check the latest tag is correct
   583  	latest, err := git.LatestTag()
   584  	if err != nil {
   585  		t.Errorf("Could not get latest tag: %v", err)
   586  	}
   587  	if latest != "v0.1.0" {
   588  		t.Errorf("Wrong latest tag: got %s, wanted %s", latest, "v0.1.0")
   589  	}
   590  }