sigs.k8s.io/release-sdk@v0.11.1-0.20240417074027-8061fb5e4952/test/integration/git_test.go (about)

     1  //go:build integration
     2  // +build integration
     3  
     4  /*
     5  Copyright 2019 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package integration
    21  
    22  import (
    23  	"os"
    24  	"path/filepath"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/blang/semver/v4"
    29  	gogit "github.com/go-git/go-git/v5"
    30  	"github.com/go-git/go-git/v5/config"
    31  	"github.com/go-git/go-git/v5/plumbing/object"
    32  	"github.com/stretchr/testify/require"
    33  
    34  	"sigs.k8s.io/release-sdk/git"
    35  	"sigs.k8s.io/release-utils/command"
    36  	"sigs.k8s.io/release-utils/util"
    37  )
    38  
    39  var testAuthor = &object.Signature{
    40  	Name:  "John Doe",
    41  	Email: "john@doe.org",
    42  	When:  time.Now(),
    43  }
    44  
    45  type testRepo struct {
    46  	sut                *git.Repo
    47  	dir                string
    48  	firstCommit        string
    49  	firstBranchCommit  string
    50  	secondBranchCommit string
    51  	thirdBranchCommit  string
    52  	branchName         string
    53  	firstTagCommit     string
    54  	firstTagName       string
    55  	secondTagCommit    string
    56  	secondTagName      string
    57  	thirdTagCommit     string
    58  	thirdTagName       string
    59  	testFileName       string
    60  }
    61  
    62  // newTestRepo creates a test repo with the following structure:
    63  //
    64  // * commit `thirdBranchCommit` (HEAD -> `branchName`, origin/`branchName`)
    65  // | Author: John Doe <john@doe.org>
    66  // |
    67  // |     Fourth commit
    68  // |
    69  // * commit `secondBranchCommit` (tag: `thirdTagName`, HEAD -> `branchName`, origin/`branchName`)
    70  // | Author: John Doe <john@doe.org>
    71  // |
    72  // |     Third commit
    73  // |
    74  // * commit `firstBranchCommit` (tag: `secondTagName`, HEAD -> `branchName`, origin/`branchName`)
    75  // | Author: John Doe <john@doe.org>
    76  // |
    77  // |     Second commit
    78  // |
    79  //
    80  //   - commit `firstCommit` (tag: `firstTagName`, origin/master, origin/HEAD, master)
    81  //     Author: John Doe <john@doe.org>
    82  //
    83  //     First commit
    84  func newTestRepo(t *testing.T) *testRepo {
    85  	// Setup the bare repo as base
    86  	bareTempDir, err := os.MkdirTemp("", "k8s-test-bare-")
    87  	require.Nil(t, err)
    88  
    89  	bareRepo, err := gogit.PlainInit(bareTempDir, true)
    90  	require.Nil(t, err)
    91  	require.NotNil(t, bareRepo)
    92  
    93  	// Clone from the bare to be able to add our test data
    94  	cloneTempDir, err := os.MkdirTemp("", "k8s-test-clone-")
    95  	require.Nil(t, err)
    96  	cloneRepo, err := gogit.PlainInit(cloneTempDir, false)
    97  	require.Nil(t, err)
    98  
    99  	// Add the test data set
   100  	const testFileName = "test-file"
   101  	require.Nil(t, os.WriteFile(
   102  		filepath.Join(cloneTempDir, testFileName),
   103  		[]byte("test-content"),
   104  		os.FileMode(0o644),
   105  	))
   106  
   107  	worktree, err := cloneRepo.Worktree()
   108  	require.Nil(t, err)
   109  	_, err = worktree.Add(testFileName)
   110  	require.Nil(t, err)
   111  
   112  	firstCommit, err := worktree.Commit("First commit", &gogit.CommitOptions{
   113  		Author: testAuthor,
   114  	})
   115  	require.Nil(t, err)
   116  
   117  	firstTagName := "v1.17.0"
   118  	firstTagRef, err := cloneRepo.CreateTag(firstTagName, firstCommit,
   119  		&gogit.CreateTagOptions{
   120  			Tagger:  testAuthor,
   121  			Message: firstTagName,
   122  		},
   123  	)
   124  	require.Nil(t, err)
   125  
   126  	// Create a test branch and a test commit on top
   127  	branchName := "release-1.17"
   128  	require.Nil(t, command.NewWithWorkDir(
   129  		cloneTempDir, "git", "checkout", "-b", branchName,
   130  	).RunSuccess())
   131  
   132  	const branchTestFileName = "branch-test-file"
   133  	require.Nil(t, os.WriteFile(
   134  		filepath.Join(cloneTempDir, branchTestFileName),
   135  		[]byte("test-content"),
   136  		os.FileMode(0o644),
   137  	))
   138  	_, err = worktree.Add(branchTestFileName)
   139  	require.Nil(t, err)
   140  
   141  	firstBranchCommit, err := worktree.Commit("Second commit", &gogit.CommitOptions{
   142  		Author: testAuthor,
   143  		All:    true,
   144  	})
   145  	require.Nil(t, err)
   146  
   147  	secondTagName := "v0.1.1"
   148  	secondTagRef, err := cloneRepo.CreateTag(secondTagName, firstBranchCommit,
   149  		&gogit.CreateTagOptions{
   150  			Tagger:  testAuthor,
   151  			Message: secondTagName,
   152  		},
   153  	)
   154  	require.Nil(t, err)
   155  
   156  	const secondBranchTestFileName = "branch-test-file-2"
   157  	require.Nil(t, os.WriteFile(
   158  		filepath.Join(cloneTempDir, secondBranchTestFileName),
   159  		[]byte("test-content"),
   160  		os.FileMode(0o644),
   161  	))
   162  	_, err = worktree.Add(secondBranchTestFileName)
   163  	require.Nil(t, err)
   164  
   165  	secondBranchCommit, err := worktree.Commit("Third commit", &gogit.CommitOptions{
   166  		Author: testAuthor,
   167  		All:    true,
   168  	})
   169  	require.Nil(t, err)
   170  
   171  	thirdTagName := "v1.17.1"
   172  	thirdTagRef, err := cloneRepo.CreateTag(thirdTagName, secondBranchCommit,
   173  		&gogit.CreateTagOptions{
   174  			Tagger:  testAuthor,
   175  			Message: thirdTagName,
   176  		},
   177  	)
   178  	require.Nil(t, err)
   179  
   180  	const thirdBranchTestFileName = "branch-test-file-3"
   181  	require.Nil(t, os.WriteFile(
   182  		filepath.Join(cloneTempDir, thirdBranchTestFileName),
   183  		[]byte("test-content"),
   184  		os.FileMode(0o644),
   185  	))
   186  	_, err = worktree.Add(thirdBranchTestFileName)
   187  	require.Nil(t, err)
   188  
   189  	thirdBranchCommit, err := worktree.Commit("Fourth commit", &gogit.CommitOptions{
   190  		Author: testAuthor,
   191  		All:    true,
   192  	})
   193  	require.Nil(t, err)
   194  
   195  	// Push the test content into the bare repo
   196  	_, err = cloneRepo.CreateRemote(&config.RemoteConfig{
   197  		Name: git.DefaultRemote,
   198  		URLs: []string{bareTempDir},
   199  	})
   200  	require.Nil(t, err)
   201  	require.Nil(t, cloneRepo.Push(&gogit.PushOptions{
   202  		RemoteName: "origin",
   203  		RefSpecs:   []config.RefSpec{"refs/*:refs/*"},
   204  	}))
   205  
   206  	require.Nil(t, os.RemoveAll(cloneTempDir))
   207  
   208  	// Provide a system under test inside the test repo
   209  	sut, err := git.CloneOrOpenRepo("", bareTempDir, false, true, nil)
   210  	require.Nil(t, err)
   211  	require.Nil(t, command.NewWithWorkDir(
   212  		sut.Dir(), "git", "checkout", branchName,
   213  	).RunSuccess())
   214  
   215  	return &testRepo{
   216  		sut:                sut,
   217  		dir:                bareTempDir,
   218  		firstCommit:        firstCommit.String(),
   219  		firstBranchCommit:  firstBranchCommit.String(),
   220  		secondBranchCommit: secondBranchCommit.String(),
   221  		thirdBranchCommit:  thirdBranchCommit.String(),
   222  		branchName:         branchName,
   223  		firstTagName:       firstTagName,
   224  		firstTagCommit:     firstTagRef.Hash().String(),
   225  		secondTagName:      secondTagName,
   226  		secondTagCommit:    secondTagRef.Hash().String(),
   227  		thirdTagName:       thirdTagName,
   228  		thirdTagCommit:     thirdTagRef.Hash().String(),
   229  		testFileName:       filepath.Join(sut.Dir(), testFileName),
   230  	}
   231  }
   232  
   233  func (r *testRepo) cleanup(t *testing.T) {
   234  	require.Nil(t, os.RemoveAll(r.dir))
   235  	require.Nil(t, os.RemoveAll(r.sut.Dir()))
   236  }
   237  
   238  func TestSuccessCloneOrOpen(t *testing.T) {
   239  	testRepo := newTestRepo(t)
   240  	defer testRepo.cleanup(t)
   241  
   242  	secondRepo, err := git.CloneOrOpenRepo(testRepo.sut.Dir(), testRepo.dir, false, true, nil)
   243  	require.Nil(t, err)
   244  
   245  	require.Equal(t, secondRepo.Dir(), testRepo.sut.Dir())
   246  	require.Nil(t, secondRepo.Cleanup())
   247  }
   248  
   249  func TestSuccessDescribeTags(t *testing.T) {
   250  	testRepo := newTestRepo(t)
   251  	defer testRepo.cleanup(t)
   252  
   253  	tag, err := testRepo.sut.Describe(
   254  		git.NewDescribeOptions().
   255  			WithRevision(testRepo.firstTagCommit).
   256  			WithAbbrev(0).
   257  			WithTags(),
   258  	)
   259  	require.Nil(t, err)
   260  	require.Equal(t, testRepo.firstTagName, tag)
   261  }
   262  
   263  func TestFailureDescribeTags(t *testing.T) {
   264  	testRepo := newTestRepo(t)
   265  	defer testRepo.cleanup(t)
   266  
   267  	_, err := testRepo.sut.Describe(
   268  		git.NewDescribeOptions().
   269  			WithRevision("wrong").
   270  			WithAbbrev(0).
   271  			WithTags(),
   272  	)
   273  	require.NotNil(t, err)
   274  }
   275  
   276  func TestSuccessHasRemoteBranch(t *testing.T) {
   277  	testRepo := newTestRepo(t)
   278  	defer testRepo.cleanup(t)
   279  
   280  	for _, repo := range []string{testRepo.branchName, git.DefaultBranch} {
   281  		branchExists, err := testRepo.sut.HasRemoteBranch(repo)
   282  		require.Nil(t, err)
   283  		require.Equal(t, branchExists, true)
   284  	}
   285  }
   286  
   287  func TestFailureHasRemoteBranch(t *testing.T) {
   288  	testRepo := newTestRepo(t)
   289  	defer testRepo.cleanup(t)
   290  
   291  	// TODO: Let's simulate an actual git/network failure
   292  
   293  	branchExists, err := testRepo.sut.HasRemoteBranch("wrong")
   294  	require.Equal(t, branchExists, false)
   295  	require.Nil(t, err)
   296  }
   297  
   298  func TestSuccessHead(t *testing.T) {
   299  	testRepo := newTestRepo(t)
   300  	defer testRepo.cleanup(t)
   301  
   302  	head, err := testRepo.sut.Head()
   303  	require.Nil(t, err)
   304  	require.Equal(t, testRepo.thirdBranchCommit, head)
   305  }
   306  
   307  func TestSuccessMerge(t *testing.T) {
   308  	testRepo := newTestRepo(t)
   309  	defer testRepo.cleanup(t)
   310  
   311  	err := testRepo.sut.Merge(git.DefaultBranch)
   312  	require.Nil(t, err)
   313  }
   314  
   315  func TestFailureMerge(t *testing.T) {
   316  	testRepo := newTestRepo(t)
   317  	defer testRepo.cleanup(t)
   318  
   319  	err := testRepo.sut.Merge("wrong")
   320  	require.NotNil(t, err)
   321  }
   322  
   323  func TestSuccessMergeBase(t *testing.T) {
   324  	testRepo := newTestRepo(t)
   325  	defer testRepo.cleanup(t)
   326  
   327  	mergeBase, err := testRepo.sut.MergeBase(git.DefaultBranch, testRepo.branchName)
   328  	require.Nil(t, err)
   329  	require.Equal(t, testRepo.firstCommit, mergeBase)
   330  }
   331  
   332  func TestSuccessRevParse(t *testing.T) {
   333  	testRepo := newTestRepo(t)
   334  	defer testRepo.cleanup(t)
   335  
   336  	mainRev, err := testRepo.sut.RevParse(git.DefaultBranch)
   337  	require.Nil(t, err)
   338  	require.Equal(t, testRepo.firstCommit, mainRev)
   339  
   340  	branchRev, err := testRepo.sut.RevParse(testRepo.branchName)
   341  	require.Nil(t, err)
   342  	require.Equal(t, testRepo.thirdBranchCommit, branchRev)
   343  
   344  	tagRev, err := testRepo.sut.RevParse(testRepo.firstTagName)
   345  	require.Nil(t, err)
   346  	require.Equal(t, testRepo.firstCommit, tagRev)
   347  
   348  	tagRev, err = testRepo.sut.RevParse(testRepo.firstCommit)
   349  	require.Nil(t, err)
   350  	require.Equal(t, testRepo.firstCommit, tagRev)
   351  }
   352  
   353  func TestSuccessRevTagParse(t *testing.T) {
   354  	testRepo := newTestRepo(t)
   355  	defer testRepo.cleanup(t)
   356  
   357  	mainRev, err := testRepo.sut.RevParseTag(git.DefaultBranch)
   358  	require.Nil(t, err)
   359  	require.Equal(t, testRepo.firstCommit, mainRev)
   360  
   361  	branchRev, err := testRepo.sut.RevParseTag(testRepo.branchName)
   362  	require.Nil(t, err)
   363  	require.Equal(t, testRepo.thirdBranchCommit, branchRev)
   364  
   365  	tagRev, err := testRepo.sut.RevParseTag(testRepo.firstTagName)
   366  	require.Nil(t, err)
   367  	require.Equal(t, testRepo.firstCommit, tagRev)
   368  }
   369  
   370  func TestFailureRevParse(t *testing.T) {
   371  	testRepo := newTestRepo(t)
   372  	defer testRepo.cleanup(t)
   373  
   374  	_, err := testRepo.sut.RevParse("wrong")
   375  	require.NotNil(t, err)
   376  }
   377  
   378  func TestFailureRevParseTag(t *testing.T) {
   379  	testRepo := newTestRepo(t)
   380  	defer testRepo.cleanup(t)
   381  
   382  	_, err := testRepo.sut.RevParseTag("wrong")
   383  	require.NotNil(t, err)
   384  
   385  	_, err = testRepo.sut.RevParseTag(testRepo.firstCommit)
   386  	require.NotNil(t, err)
   387  }
   388  
   389  func TestSuccessRevParseShort(t *testing.T) {
   390  	testRepo := newTestRepo(t)
   391  	defer testRepo.cleanup(t)
   392  
   393  	mainRev, err := testRepo.sut.RevParseShort(git.DefaultBranch)
   394  	require.Nil(t, err)
   395  	require.Equal(t, testRepo.firstCommit[:10], mainRev)
   396  
   397  	branchRev, err := testRepo.sut.RevParseShort(testRepo.branchName)
   398  	require.Nil(t, err)
   399  	require.Equal(t, testRepo.thirdBranchCommit[:10], branchRev)
   400  
   401  	tagRev, err := testRepo.sut.RevParseShort(testRepo.firstTagName)
   402  	require.Nil(t, err)
   403  	require.Equal(t, testRepo.firstCommit[:10], tagRev)
   404  
   405  	tagRev, err = testRepo.sut.RevParseShort(testRepo.firstCommit)
   406  	require.Nil(t, err)
   407  	require.Equal(t, testRepo.firstCommit[:10], tagRev)
   408  }
   409  
   410  func TestSuccessRevParseTagShort(t *testing.T) {
   411  	testRepo := newTestRepo(t)
   412  	defer testRepo.cleanup(t)
   413  
   414  	mainRev, err := testRepo.sut.RevParseTagShort(git.DefaultBranch)
   415  	require.Nil(t, err)
   416  	require.Equal(t, testRepo.firstCommit[:10], mainRev)
   417  
   418  	branchRev, err := testRepo.sut.RevParseTagShort(testRepo.branchName)
   419  	require.Nil(t, err)
   420  	require.Equal(t, testRepo.thirdBranchCommit[:10], branchRev)
   421  
   422  	tagRev, err := testRepo.sut.RevParseTagShort(testRepo.firstTagName)
   423  	require.Nil(t, err)
   424  	require.Equal(t, testRepo.firstCommit[:10], tagRev)
   425  }
   426  
   427  func TestFailureRevParseShort(t *testing.T) {
   428  	testRepo := newTestRepo(t)
   429  	defer testRepo.cleanup(t)
   430  
   431  	_, err := testRepo.sut.RevParseShort("wrong")
   432  	require.NotNil(t, err)
   433  }
   434  
   435  func TestFailureRevParseTagShort(t *testing.T) {
   436  	testRepo := newTestRepo(t)
   437  	defer testRepo.cleanup(t)
   438  
   439  	_, err := testRepo.sut.RevParseTagShort("wrong")
   440  	require.NotNil(t, err)
   441  
   442  	_, err = testRepo.sut.RevParseTagShort(testRepo.firstCommit)
   443  	require.NotNil(t, err)
   444  }
   445  
   446  func TestSuccessPush(t *testing.T) {
   447  	testRepo := newTestRepo(t)
   448  	defer testRepo.cleanup(t)
   449  
   450  	err := testRepo.sut.Push(git.DefaultBranch)
   451  	require.Nil(t, err)
   452  }
   453  
   454  func TestFailurePush(t *testing.T) {
   455  	testRepo := newTestRepo(t)
   456  	defer testRepo.cleanup(t)
   457  
   458  	err := testRepo.sut.Push("wrong")
   459  	require.NotNil(t, err)
   460  }
   461  
   462  func TestSuccessRemotify(t *testing.T) {
   463  	newRemote := git.Remotify(git.DefaultBranch)
   464  	require.Equal(t, git.DefaultRemote+"/"+git.DefaultBranch, newRemote)
   465  }
   466  
   467  func TestSuccessIsReleaseBranch(t *testing.T) {
   468  	require.True(t, git.IsReleaseBranch("release-1.17"))
   469  }
   470  
   471  func TestFailureIsReleaseBranch(t *testing.T) {
   472  	require.False(t, git.IsReleaseBranch("wrong-branch"))
   473  }
   474  
   475  func TestSuccessLatestTagForBranch(t *testing.T) {
   476  	testRepo := newTestRepo(t)
   477  	defer testRepo.cleanup(t)
   478  
   479  	version, err := testRepo.sut.LatestTagForBranch(git.DefaultBranch)
   480  	require.Nil(t, err)
   481  	require.Equal(t, testRepo.firstTagName, util.SemverToTagString(version))
   482  }
   483  
   484  func TestSuccessLatestTagForBranchRelease(t *testing.T) {
   485  	testRepo := newTestRepo(t)
   486  	defer testRepo.cleanup(t)
   487  
   488  	version, err := testRepo.sut.LatestTagForBranch("release-1.17")
   489  	require.Nil(t, err)
   490  	require.Equal(t, testRepo.thirdTagName, util.SemverToTagString(version))
   491  }
   492  
   493  func TestFailureLatestTagForBranchInvalidBranch(t *testing.T) {
   494  	testRepo := newTestRepo(t)
   495  	defer testRepo.cleanup(t)
   496  
   497  	version, err := testRepo.sut.LatestTagForBranch("wrong-branch")
   498  	require.NotNil(t, err)
   499  	require.Equal(t, semver.Version{}, version)
   500  }
   501  
   502  func TestSuccessLatestPatchToPatch(t *testing.T) {
   503  	testRepo := newTestRepo(t)
   504  	defer testRepo.cleanup(t)
   505  
   506  	// This test case gets commits from v1.17.0 to v1.17.1
   507  	result, err := testRepo.sut.LatestPatchToPatch(testRepo.branchName)
   508  	require.Nil(t, err)
   509  	require.Equal(t, testRepo.firstCommit, result.StartSHA())
   510  	require.Equal(t, testRepo.firstTagName, result.StartRev())
   511  	require.Equal(t, testRepo.thirdTagName, result.EndRev())
   512  }
   513  
   514  func TestSuccessLatestPatchToPatchNewTag(t *testing.T) {
   515  	testRepo := newTestRepo(t)
   516  	defer testRepo.cleanup(t)
   517  
   518  	// This test case gets commits from v1.17.1 to a new v1.17.2
   519  	nextMinorTag := "v1.17.2"
   520  	require.Nil(t, command.NewWithWorkDir(
   521  		testRepo.sut.Dir(), "git", "tag", nextMinorTag,
   522  	).RunSuccess())
   523  
   524  	result, err := testRepo.sut.LatestPatchToPatch(testRepo.branchName)
   525  	require.Nil(t, err)
   526  	require.Equal(t, testRepo.secondBranchCommit, result.StartSHA())
   527  	require.Equal(t, testRepo.thirdTagName, result.StartRev())
   528  	require.Equal(t, nextMinorTag, result.EndRev())
   529  }
   530  
   531  func TestFailureLatestPatchToPatchWrongBranch(t *testing.T) {
   532  	testRepo := newTestRepo(t)
   533  	defer testRepo.cleanup(t)
   534  
   535  	result, err := testRepo.sut.LatestPatchToPatch("wrong-branch")
   536  	require.NotNil(t, err)
   537  	require.Equal(t, result, git.DiscoverResult{})
   538  }
   539  
   540  func TestSuccessLatestPatchToLatest(t *testing.T) {
   541  	testRepo := newTestRepo(t)
   542  	defer testRepo.cleanup(t)
   543  
   544  	// This test case gets commits from v1.17.1 to head of release-1.17
   545  	result, err := testRepo.sut.LatestPatchToLatest(testRepo.branchName)
   546  	require.Nil(t, err)
   547  	require.Equal(t, testRepo.secondBranchCommit, result.StartSHA())
   548  	require.Equal(t, testRepo.thirdTagName, result.StartRev())
   549  	require.Equal(t, testRepo.thirdBranchCommit, result.EndSHA())
   550  }
   551  
   552  func TestSuccessDry(t *testing.T) {
   553  	testRepo := newTestRepo(t)
   554  	defer testRepo.cleanup(t)
   555  
   556  	testRepo.sut.SetDry()
   557  
   558  	err := testRepo.sut.Push(git.DefaultBranch)
   559  	require.Nil(t, err)
   560  }
   561  
   562  func TestSuccessLatestReleaseBranchMergeBaseToLatest(t *testing.T) {
   563  	testRepo := newTestRepo(t)
   564  	defer testRepo.cleanup(t)
   565  
   566  	result, err := testRepo.sut.LatestReleaseBranchMergeBaseToLatest()
   567  	require.Nil(t, err)
   568  	require.Equal(t, testRepo.firstCommit, result.StartSHA())
   569  	require.Equal(t, testRepo.firstTagName, result.StartRev())
   570  	require.Equal(t, testRepo.firstCommit, result.EndSHA())
   571  	require.Equal(t, git.DefaultBranch, result.EndRev())
   572  }
   573  
   574  func TestFailureLatestReleaseBranchMergeBaseToLatestNoLatestTag(t *testing.T) {
   575  	testRepo := newTestRepo(t)
   576  	defer testRepo.cleanup(t)
   577  
   578  	require.Nil(t, command.NewWithWorkDir(
   579  		testRepo.sut.Dir(), "git", "tag", "-d", testRepo.firstTagName,
   580  	).RunSuccess())
   581  
   582  	result, err := testRepo.sut.LatestReleaseBranchMergeBaseToLatest()
   583  	require.NotNil(t, err)
   584  	require.Equal(t, result, git.DiscoverResult{})
   585  }
   586  
   587  func TestSuccessLatestNonPatchFinalToMinor(t *testing.T) {
   588  	testRepo := newTestRepo(t)
   589  	defer testRepo.cleanup(t)
   590  
   591  	nextMinorTag := "v1.18.0"
   592  	require.Nil(t, command.NewWithWorkDir(
   593  		testRepo.sut.Dir(), "git", "tag", nextMinorTag,
   594  	).RunSuccess())
   595  
   596  	result, err := testRepo.sut.LatestNonPatchFinalToMinor()
   597  	require.Nil(t, err)
   598  	require.Equal(t, testRepo.firstCommit, result.StartSHA())
   599  	require.Equal(t, testRepo.firstTagName, result.StartRev())
   600  	require.Equal(t, nextMinorTag, result.EndRev())
   601  }
   602  
   603  func TestFailureLatestNonPatchFinalToMinor(t *testing.T) {
   604  	testRepo := newTestRepo(t)
   605  	defer testRepo.cleanup(t)
   606  
   607  	result, err := testRepo.sut.LatestNonPatchFinalToMinor()
   608  	require.NotNil(t, err)
   609  	require.Equal(t, result, git.DiscoverResult{})
   610  }
   611  
   612  func TestTagsForBranchMain(t *testing.T) {
   613  	testRepo := newTestRepo(t)
   614  	defer testRepo.cleanup(t)
   615  
   616  	result, err := testRepo.sut.TagsForBranch(git.DefaultBranch)
   617  	require.Nil(t, err)
   618  	require.Equal(t, []string{testRepo.firstTagName}, result)
   619  }
   620  
   621  func TestTagsForBranchOnBranch(t *testing.T) {
   622  	testRepo := newTestRepo(t)
   623  	defer testRepo.cleanup(t)
   624  
   625  	result, err := testRepo.sut.TagsForBranch(testRepo.branchName)
   626  	require.Nil(t, err)
   627  	require.Equal(t, []string{
   628  		testRepo.thirdTagName,
   629  		testRepo.firstTagName,
   630  		testRepo.secondTagName,
   631  	}, result)
   632  }
   633  
   634  func TestTagsForBranchFailureWrongBranch(t *testing.T) {
   635  	testRepo := newTestRepo(t)
   636  	defer testRepo.cleanup(t)
   637  
   638  	result, err := testRepo.sut.TagsForBranch("wrong-branch")
   639  	require.NotNil(t, err)
   640  	require.Nil(t, result)
   641  }
   642  
   643  func TestCheckoutSuccess(t *testing.T) {
   644  	testRepo := newTestRepo(t)
   645  	defer testRepo.cleanup(t)
   646  
   647  	require.Nil(t, os.WriteFile(
   648  		testRepo.testFileName,
   649  		[]byte("hello world"),
   650  		os.FileMode(0o644),
   651  	))
   652  	res, err := command.NewWithWorkDir(
   653  		testRepo.sut.Dir(), "git", "diff", "--name-only").Run()
   654  	require.Nil(t, err)
   655  	require.True(t, res.Success())
   656  	require.Contains(t, res.Output(), filepath.Base(testRepo.testFileName))
   657  
   658  	err = testRepo.sut.Checkout(git.DefaultBranch, testRepo.testFileName)
   659  	require.Nil(t, err)
   660  
   661  	res, err = command.NewWithWorkDir(
   662  		testRepo.sut.Dir(), "git", "diff", "--name-only").Run()
   663  	require.Nil(t, err)
   664  	require.True(t, res.Success())
   665  	require.Empty(t, res.Output())
   666  }
   667  
   668  func TestCheckoutFailureWrongRevision(t *testing.T) {
   669  	testRepo := newTestRepo(t)
   670  	defer testRepo.cleanup(t)
   671  
   672  	err := testRepo.sut.Checkout("wrong")
   673  	require.NotNil(t, err)
   674  	require.Contains(t, err.Error(), "checkout wrong did not succeed")
   675  }
   676  
   677  func TestAddSuccess(t *testing.T) {
   678  	testRepo := newTestRepo(t)
   679  	defer testRepo.cleanup(t)
   680  
   681  	f, err := os.CreateTemp(testRepo.sut.Dir(), "test")
   682  	require.Nil(t, err)
   683  	require.Nil(t, f.Close())
   684  
   685  	filename := filepath.Base(f.Name())
   686  	err = testRepo.sut.Add(filename)
   687  	require.Nil(t, err)
   688  
   689  	res, err := command.NewWithWorkDir(
   690  		testRepo.sut.Dir(), "git", "diff", "--cached", "--name-only").Run()
   691  	require.Nil(t, err)
   692  	require.True(t, res.Success())
   693  	require.Contains(t, res.Output(), filename)
   694  }
   695  
   696  func TestAddFailureWrongPath(t *testing.T) {
   697  	testRepo := newTestRepo(t)
   698  	defer testRepo.cleanup(t)
   699  
   700  	err := testRepo.sut.Add("wrong")
   701  	require.NotNil(t, err)
   702  	require.Contains(t, err.Error(), "adding file wrong to repository")
   703  }
   704  
   705  func TestCommitSuccess(t *testing.T) {
   706  	testRepo := newTestRepo(t)
   707  	defer testRepo.cleanup(t)
   708  
   709  	commitMessage := "My commit message for this test"
   710  	err := testRepo.sut.Commit(commitMessage)
   711  	require.Nil(t, err)
   712  
   713  	res, err := command.NewWithWorkDir(
   714  		testRepo.sut.Dir(), "git", "log", "-1",
   715  	).Run()
   716  	require.Nil(t, err)
   717  	require.True(t, res.Success())
   718  	require.Contains(
   719  		t,
   720  		res.Output(),
   721  		"Author: Kubernetes Release Robot <k8s-release-robot@users.noreply.github.com>",
   722  	)
   723  	require.Contains(
   724  		t,
   725  		res.Output(),
   726  		commitMessage,
   727  	)
   728  }
   729  
   730  func TestCurrentBranchDefault(t *testing.T) {
   731  	testRepo := newTestRepo(t)
   732  	defer testRepo.cleanup(t)
   733  
   734  	branch, err := testRepo.sut.CurrentBranch()
   735  	require.Nil(t, err)
   736  	require.Equal(t, branch, testRepo.branchName)
   737  }
   738  
   739  func TestCurrentBranchMain(t *testing.T) {
   740  	testRepo := newTestRepo(t)
   741  	defer testRepo.cleanup(t)
   742  	require.Nil(t, testRepo.sut.Checkout(git.DefaultBranch))
   743  
   744  	branch, err := testRepo.sut.CurrentBranch()
   745  	require.Nil(t, err)
   746  	require.Equal(t, branch, git.DefaultBranch)
   747  }
   748  
   749  func TestRmSuccessForce(t *testing.T) {
   750  	testRepo := newTestRepo(t)
   751  	defer testRepo.cleanup(t)
   752  	require.Nil(t, os.WriteFile(testRepo.testFileName,
   753  		[]byte("test"), os.FileMode(0o755)),
   754  	)
   755  
   756  	require.Nil(t, testRepo.sut.Rm(true, testRepo.testFileName))
   757  
   758  	_, err := os.Stat(testRepo.testFileName)
   759  	require.True(t, os.IsNotExist(err))
   760  }
   761  
   762  func TestHasRemoteSuccess(t *testing.T) {
   763  	testRepo := newTestRepo(t)
   764  	defer testRepo.cleanup(t)
   765  
   766  	err := testRepo.sut.AddRemote("test", "owner", "repo", true)
   767  	require.Nil(t, err)
   768  
   769  	remotes, err := testRepo.sut.Remotes()
   770  	require.Nil(t, err)
   771  
   772  	require.Len(t, remotes, 2)
   773  
   774  	// The origin remote
   775  	require.Equal(t, remotes[0].Name(), git.DefaultRemote)
   776  	require.Len(t, remotes[0].URLs(), 1)
   777  	require.Equal(t, remotes[0].URLs()[0], testRepo.dir)
   778  
   779  	// Or via the API
   780  	require.True(t, testRepo.sut.HasRemote("origin", testRepo.dir))
   781  
   782  	// The added test remote
   783  	require.Equal(t, remotes[1].Name(), "test")
   784  	require.Len(t, remotes[1].URLs(), 1)
   785  
   786  	url := git.GetRepoURL("owner", "repo", true)
   787  	require.Equal(t, remotes[1].URLs()[0], url)
   788  
   789  	// Or via the API
   790  	require.True(t, testRepo.sut.HasRemote("test", url))
   791  }
   792  
   793  func TestHasRemoteFailure(t *testing.T) {
   794  	testRepo := newTestRepo(t)
   795  	defer testRepo.cleanup(t)
   796  
   797  	require.False(t, testRepo.sut.HasRemote("name", "some-url.com"))
   798  }
   799  
   800  func TestRmFailureForce(t *testing.T) {
   801  	testRepo := newTestRepo(t)
   802  	defer testRepo.cleanup(t)
   803  	require.NotNil(t, testRepo.sut.Rm(true, "invalid"))
   804  }
   805  
   806  func TestRmSuccess(t *testing.T) {
   807  	testRepo := newTestRepo(t)
   808  	defer testRepo.cleanup(t)
   809  
   810  	require.Nil(t, testRepo.sut.Rm(true, testRepo.testFileName))
   811  
   812  	_, err := os.Stat(testRepo.testFileName)
   813  	require.True(t, os.IsNotExist(err))
   814  }
   815  
   816  func TestRmFailureModified(t *testing.T) {
   817  	testRepo := newTestRepo(t)
   818  	defer testRepo.cleanup(t)
   819  	require.Nil(t, os.WriteFile(testRepo.testFileName,
   820  		[]byte("test"), os.FileMode(0o755)),
   821  	)
   822  	require.NotNil(t, testRepo.sut.Rm(false, testRepo.testFileName))
   823  }
   824  
   825  func TestOpenRepoSuccess(t *testing.T) {
   826  	testRepo := newTestRepo(t)
   827  	defer testRepo.cleanup(t)
   828  
   829  	repo, err := git.OpenRepo(testRepo.sut.Dir())
   830  	require.Nil(t, err)
   831  	require.Equal(t, repo.Dir(), testRepo.sut.Dir())
   832  }
   833  
   834  func TestOpenRepoSuccessSearchGitDot(t *testing.T) {
   835  	testRepo := newTestRepo(t)
   836  	defer testRepo.cleanup(t)
   837  
   838  	repo, err := git.OpenRepo(filepath.Join(testRepo.sut.Dir(), "not-existing"))
   839  	require.Nil(t, err)
   840  	require.Equal(t, repo.Dir(), testRepo.sut.Dir())
   841  }
   842  
   843  func TestOpenRepoFailure(t *testing.T) {
   844  	repo, err := git.OpenRepo("/invalid")
   845  	require.NotNil(t, err)
   846  	require.Nil(t, repo)
   847  }
   848  
   849  func TestAddRemoteSuccess(t *testing.T) {
   850  	testRepo := newTestRepo(t)
   851  	defer testRepo.cleanup(t)
   852  
   853  	err := testRepo.sut.AddRemote("remote", "owner", "repo", true)
   854  	require.Nil(t, err)
   855  }
   856  
   857  func TestAddRemoteFailureAlreadyExisting(t *testing.T) {
   858  	testRepo := newTestRepo(t)
   859  	defer testRepo.cleanup(t)
   860  
   861  	err := testRepo.sut.AddRemote(git.DefaultRemote, "owner", "repo", true)
   862  	require.NotNil(t, err)
   863  }
   864  
   865  func TestPushToRemoteSuccessRemoteMain(t *testing.T) {
   866  	testRepo := newTestRepo(t)
   867  	defer testRepo.cleanup(t)
   868  
   869  	err := testRepo.sut.PushToRemote(git.DefaultRemote, git.Remotify(git.DefaultBranch))
   870  	require.Nil(t, err)
   871  }
   872  
   873  func TestPushToRemoteSuccessBranchTracked(t *testing.T) {
   874  	testRepo := newTestRepo(t)
   875  	defer testRepo.cleanup(t)
   876  
   877  	err := testRepo.sut.PushToRemote(git.DefaultRemote, testRepo.branchName)
   878  	require.Nil(t, err)
   879  }
   880  
   881  func TestPushToRemoteFailureBranchNotExisting(t *testing.T) {
   882  	testRepo := newTestRepo(t)
   883  	defer testRepo.cleanup(t)
   884  
   885  	err := testRepo.sut.PushToRemote(git.DefaultRemote, "some-branch")
   886  	require.NotNil(t, err)
   887  }
   888  
   889  func TestLSRemoteSuccess(t *testing.T) {
   890  	testRepo := newTestRepo(t)
   891  	defer testRepo.cleanup(t)
   892  
   893  	res, err := testRepo.sut.LsRemote()
   894  	require.Nil(t, err)
   895  	require.Contains(t, res, testRepo.firstCommit)
   896  	require.Contains(t, res, testRepo.secondBranchCommit)
   897  	require.Contains(t, res, testRepo.thirdBranchCommit)
   898  }
   899  
   900  func TestLSRemoteFailure(t *testing.T) {
   901  	testRepo := newTestRepo(t)
   902  	defer testRepo.cleanup(t)
   903  
   904  	res, err := testRepo.sut.LsRemote("invalid")
   905  	require.NotNil(t, err)
   906  	require.Empty(t, res)
   907  }
   908  
   909  func TestBranchSuccess(t *testing.T) {
   910  	testRepo := newTestRepo(t)
   911  	defer testRepo.cleanup(t)
   912  
   913  	res, err := testRepo.sut.Branch()
   914  	require.Nil(t, err)
   915  	require.Contains(t, res, testRepo.branchName)
   916  }
   917  
   918  func TestBranchFailure(t *testing.T) {
   919  	testRepo := newTestRepo(t)
   920  	defer testRepo.cleanup(t)
   921  
   922  	res, err := testRepo.sut.Branch("--invalid")
   923  	require.NotNil(t, err)
   924  	require.Empty(t, res)
   925  }
   926  
   927  func TestIsDirtySuccess(t *testing.T) {
   928  	testRepo := newTestRepo(t)
   929  	defer testRepo.cleanup(t)
   930  
   931  	dirty, err := testRepo.sut.IsDirty()
   932  	require.Nil(t, err)
   933  	require.False(t, dirty)
   934  }
   935  
   936  func TestIsDirtyFailure(t *testing.T) {
   937  	testRepo := newTestRepo(t)
   938  	defer testRepo.cleanup(t)
   939  
   940  	require.Nil(t, os.WriteFile(
   941  		filepath.Join(testRepo.sut.Dir(), "any-file"),
   942  		[]byte("test"), os.FileMode(0o755)),
   943  	)
   944  
   945  	dirty, err := testRepo.sut.IsDirty()
   946  	require.Nil(t, err)
   947  	require.True(t, dirty)
   948  }
   949  
   950  func TestSetURLSuccess(t *testing.T) {
   951  	testRepo := newTestRepo(t)
   952  	defer testRepo.cleanup(t)
   953  
   954  	const remote = "https://example.com"
   955  	require.Nil(t, testRepo.sut.SetURL(git.DefaultRemote, remote))
   956  	remotes, err := testRepo.sut.Remotes()
   957  	require.Nil(t, err)
   958  	require.Len(t, remotes, 1)
   959  	require.Equal(t, git.DefaultRemote, remotes[0].Name())
   960  	require.Len(t, remotes[0].URLs(), 1)
   961  	require.Equal(t, remotes[0].URLs()[0], remote)
   962  }
   963  
   964  func TestSetURLFailureRemoteDoesNotExists(t *testing.T) {
   965  	testRepo := newTestRepo(t)
   966  	defer testRepo.cleanup(t)
   967  
   968  	require.NotNil(t, testRepo.sut.SetURL("some-remote", ""))
   969  }
   970  
   971  func TestAllTags(t *testing.T) {
   972  	testRepo := newTestRepo(t)
   973  	defer testRepo.cleanup(t)
   974  
   975  	tags, err := testRepo.sut.Tags()
   976  	require.Nil(t, err)
   977  	require.Len(t, tags, 3)
   978  	require.Equal(t, tags[0], testRepo.secondTagName)
   979  	require.Equal(t, tags[1], testRepo.firstTagName)
   980  	require.Equal(t, tags[2], testRepo.thirdTagName)
   981  }
   982  
   983  func TestCommitEmptySuccess(t *testing.T) {
   984  	testRepo := newTestRepo(t)
   985  	defer testRepo.cleanup(t)
   986  
   987  	commitMessage := "This is an empty commit"
   988  	require.Nil(t, testRepo.sut.CommitEmpty(commitMessage))
   989  	res, err := command.NewWithWorkDir(
   990  		testRepo.sut.Dir(), "git", "log", "-1",
   991  	).Run()
   992  	require.Nil(t, err)
   993  	require.True(t, res.Success())
   994  	require.Contains(t, res.Output(), commitMessage)
   995  }
   996  
   997  func TestTagSuccess(t *testing.T) {
   998  	testRepo := newTestRepo(t)
   999  	defer testRepo.cleanup(t)
  1000  
  1001  	testTag := "testTag"
  1002  	require.Nil(t, testRepo.sut.Tag(testTag, "message"))
  1003  	tags, err := testRepo.sut.TagsForBranch(testRepo.branchName)
  1004  	require.Nil(t, err)
  1005  	require.Contains(t, tags, testTag)
  1006  }
  1007  
  1008  func TestLatestReleaseBranch(t *testing.T) {
  1009  	testRepo := newTestRepo(t)
  1010  	defer testRepo.cleanup(t)
  1011  
  1012  	branch, err := testRepo.sut.LatestReleaseBranch()
  1013  	require.Nil(t, err)
  1014  	require.Equal(t, testRepo.branchName, branch)
  1015  }