github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/git/git_test.go (about)

     1  package git_test
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/treeverse/lakefs/pkg/git"
    13  )
    14  
    15  func TestIsRepository(t *testing.T) {
    16  	tmpdir := t.TempDir()
    17  	tmpSubdir, err := os.MkdirTemp(tmpdir, "")
    18  	require.NoError(t, err)
    19  	defer func(name string) {
    20  		err = os.RemoveAll(name)
    21  		if err != nil {
    22  			t.Error("failed to remove temp dir", err)
    23  		}
    24  	}(tmpSubdir)
    25  	tmpFile, err := os.CreateTemp(tmpSubdir, "")
    26  	require.NoError(t, err)
    27  	defer func() {
    28  		_ = os.Remove(tmpFile.Name())
    29  		_ = tmpFile.Close()
    30  	}()
    31  
    32  	require.False(t, git.IsRepository(tmpFile.Name()))
    33  	require.False(t, git.IsRepository(tmpdir))
    34  
    35  	// Init git repo on root
    36  	require.NoError(t, exec.Command("git", "init", "-q", tmpdir).Run())
    37  	require.False(t, git.IsRepository(tmpFile.Name()))
    38  	require.True(t, git.IsRepository(tmpdir))
    39  	require.True(t, git.IsRepository(tmpSubdir))
    40  }
    41  
    42  func TestGetRepositoryPath(t *testing.T) {
    43  	var err error
    44  	tmpdir := t.TempDir()
    45  	tmpdir, err = filepath.EvalSymlinks(tmpdir) // on macOS tmpdir is a symlink
    46  	require.NoError(t, err)
    47  	tmpSubdir, err := os.MkdirTemp(tmpdir, "")
    48  	require.NoError(t, err)
    49  	defer func(name string) {
    50  		_ = os.Remove(name)
    51  	}(tmpSubdir)
    52  	tmpFile, err := os.CreateTemp(tmpSubdir, "")
    53  	require.NoError(t, err)
    54  	defer func() {
    55  		_ = os.Remove(tmpFile.Name())
    56  		_ = tmpFile.Close()
    57  	}()
    58  
    59  	_, err = git.GetRepositoryPath(tmpdir)
    60  	require.ErrorIs(t, err, git.ErrNotARepository)
    61  	_, err = git.GetRepositoryPath(tmpFile.Name())
    62  	require.Error(t, err)
    63  
    64  	// Init git repo on root
    65  	require.NoError(t, exec.Command("git", "init", "-q", tmpdir).Run())
    66  	gitPath, err := git.GetRepositoryPath(tmpdir)
    67  	require.NoError(t, err)
    68  	require.Equal(t, tmpdir, gitPath)
    69  	_, err = git.GetRepositoryPath(tmpFile.Name())
    70  	require.Error(t, err)
    71  	gitPath, err = git.GetRepositoryPath(tmpSubdir)
    72  	require.NoError(t, err)
    73  	require.Equal(t, tmpdir, gitPath)
    74  }
    75  
    76  func TestIgnore(t *testing.T) {
    77  	const (
    78  		excludedFile = ".excluded.ex"
    79  		trackedFile  = "file1"
    80  		marker       = "Test Marker"
    81  	)
    82  	tmpdir, err := filepath.EvalSymlinks(t.TempDir()) // on macOS tmpdir is a symlink
    83  	require.NoError(t, err)
    84  	// create sub dir and file
    85  	tmpSubdir, err := os.MkdirTemp(tmpdir, "")
    86  	require.NoError(t, err)
    87  	tmpFile, err := os.CreateTemp(tmpSubdir, "")
    88  	require.NoError(t, err)
    89  	excludedPath := filepath.Join(tmpSubdir, excludedFile)
    90  
    91  	// Test we can't ignore if not a git repo
    92  	_, err = git.Ignore(tmpdir, []string{}, []string{}, marker)
    93  	require.ErrorIs(t, err, git.ErrNotARepository)
    94  	_, err = git.Ignore(tmpFile.Name(), []string{}, []string{}, marker)
    95  	require.Error(t, err)
    96  
    97  	// Init git repo on tmpdir
    98  	err = exec.Command("git", "init", "-q", tmpdir).Run()
    99  	require.NoError(t, err)
   100  	ignorePath := filepath.Join(tmpdir, git.IgnoreFile)
   101  
   102  	// Create files in repo
   103  	for _, fn := range []string{
   104  		filepath.Join(tmpdir, trackedFile),
   105  		filepath.Join(tmpSubdir, "should_be_ignored"),
   106  		excludedPath,
   107  	} {
   108  		err = os.WriteFile(fn, []byte("content\n"), 0o644)
   109  		require.NoError(t, err, "failed to create file %s", fn)
   110  	}
   111  
   112  	// Changing the working directory
   113  	require.NoError(t, os.Chdir(tmpdir))
   114  	verifyPathTracked(t, []string{filepath.Base(tmpSubdir), trackedFile})
   115  
   116  	_, err = git.Ignore(tmpFile.Name(), []string{}, []string{excludedPath}, marker)
   117  	require.Error(t, err)
   118  	result, err := git.Ignore(tmpdir, []string{}, []string{excludedPath}, marker)
   119  	require.NoError(t, err)
   120  	require.Equal(t, ignorePath, result)
   121  	relExcludedPath, err := filepath.Rel(tmpdir, excludedPath)
   122  	require.NoError(t, err)
   123  
   124  	verifyPathTracked(t, []string{filepath.Base(tmpSubdir), trackedFile})
   125  
   126  	_, err = git.Ignore(tmpSubdir, []string{tmpFile.Name()}, []string{excludedPath}, marker)
   127  	require.NoError(t, err)
   128  	relDataFile, err := filepath.Rel(tmpdir, tmpFile.Name())
   129  	require.NoError(t, err)
   130  	verifyPathTracked(t, []string{trackedFile})
   131  
   132  	_, err = git.Ignore(tmpSubdir, []string{tmpSubdir, filepath.Join(tmpdir, trackedFile)}, []string{}, marker)
   133  	require.NoError(t, err)
   134  	relDataSubDir, err := filepath.Rel(tmpdir, tmpSubdir)
   135  	require.NoError(t, err)
   136  	verifyPathTracked(t, []string{git.IgnoreFile})
   137  
   138  	_, err = git.Ignore(tmpSubdir, []string{tmpSubdir, filepath.Join(tmpdir, trackedFile), ignorePath}, []string{}, marker)
   139  	require.NoError(t, err)
   140  	require.Equal(t, ignorePath, result)
   141  
   142  	contents, err := os.ReadFile(ignorePath)
   143  	require.NoError(t, err)
   144  	verifyPathTracked(t, nil)
   145  
   146  	expectedGitIgnore := fmt.Sprintf("# %s\n%s\n!%s\n%s\n%s\n%s\n# End %s\n",
   147  		marker,
   148  		relDataFile, relExcludedPath, filepath.Join(relDataSubDir, "*"), trackedFile, git.IgnoreFile,
   149  		marker)
   150  	require.Equal(t, expectedGitIgnore, string(contents))
   151  }
   152  
   153  func verifyPathTracked(t *testing.T, paths []string) {
   154  	cmd := exec.Command("git", "status")
   155  	r, w, _ := os.Pipe()
   156  	cmd.Stdout = w
   157  	require.NoError(t, cmd.Run())
   158  	require.NoError(t, w.Close())
   159  	out, err := io.ReadAll(r)
   160  	require.NoError(t, err)
   161  	outStr := string(out)
   162  
   163  	if len(paths) == 0 {
   164  		require.Contains(t, outStr, "nothing to commit")
   165  	} else {
   166  		for _, p := range paths {
   167  			require.Contains(t, outStr, p)
   168  		}
   169  	}
   170  }
   171  
   172  func TestParseURL(t *testing.T) {
   173  	cases := []struct {
   174  		Url         string
   175  		ExpectedUrl *git.URL
   176  	}{
   177  		{
   178  			Url: "git@github.com:treeverse/lakeFS.git",
   179  			ExpectedUrl: &git.URL{
   180  				Server:  "github.com",
   181  				Owner:   "treeverse",
   182  				Project: "lakeFS",
   183  			},
   184  		},
   185  		{
   186  			Url: "ssh://git@github.com/tree/lake.git",
   187  			ExpectedUrl: &git.URL{
   188  				Server:  "github.com",
   189  				Owner:   "tree",
   190  				Project: "lake",
   191  			},
   192  		},
   193  		{
   194  			Url: "https://github.com/treeverse/lakeFS2.git",
   195  			ExpectedUrl: &git.URL{
   196  				Server:  "github.com",
   197  				Owner:   "treeverse",
   198  				Project: "lakeFS2",
   199  			},
   200  		},
   201  		{
   202  			Url: "git://git@192.168.1.20:MyGroup/MyProject.git",
   203  			ExpectedUrl: &git.URL{
   204  				Server:  "192.168.1.20",
   205  				Owner:   "MyGroup",
   206  				Project: "MyProject",
   207  			},
   208  		},
   209  		{
   210  			Url: "git://git@192.168.1.20:22:MyGroup/MyProject.git",
   211  			ExpectedUrl: &git.URL{
   212  				Server:  "192.168.1.20:22",
   213  				Owner:   "MyGroup",
   214  				Project: "MyProject",
   215  			},
   216  		},
   217  		{
   218  			Url: "git://git@github.com:Hyphened-Owner/Hyphened-Project.git",
   219  			ExpectedUrl: &git.URL{
   220  				Server:  "github.com",
   221  				Owner:   "Hyphened-Owner",
   222  				Project: "Hyphened-Project",
   223  			},
   224  		},
   225  		{
   226  			Url:         "bad_url",
   227  			ExpectedUrl: nil,
   228  		},
   229  	}
   230  	for _, tt := range cases {
   231  		t.Run(tt.Url, func(t *testing.T) {
   232  			parsed := git.ParseURL(tt.Url)
   233  			require.Equal(t, tt.ExpectedUrl, parsed)
   234  		})
   235  	}
   236  }