github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libgit/browser_test.go (about)

     1  // Copyright 2018 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package libgit
     6  
     7  import (
     8  	"io"
     9  	"os"
    10  	"path"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/keybase/client/go/kbfs/data"
    15  	"github.com/keybase/client/go/kbfs/libfs"
    16  	"github.com/keybase/client/go/kbfs/libkbfs"
    17  	"github.com/keybase/client/go/kbfs/tlf"
    18  	"github.com/keybase/client/go/kbfs/tlfhandle"
    19  	"github.com/keybase/client/go/protocol/keybase1"
    20  	"github.com/stretchr/testify/require"
    21  	gogit "gopkg.in/src-d/go-git.v4"
    22  	"gopkg.in/src-d/go-git.v4/plumbing/object"
    23  )
    24  
    25  func testBrowser(t *testing.T, sharedCache sharedInBrowserCache) {
    26  	ctx, config, cancel, tempdir := initConfigForAutogit(t)
    27  	defer cancel()
    28  	defer os.RemoveAll(tempdir)
    29  	defer libkbfs.CheckConfigAndShutdown(ctx, t, config)
    30  
    31  	h, err := tlfhandle.ParseHandle(
    32  		ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private)
    33  	require.NoError(t, err)
    34  	rootFS, err := libfs.NewFS(
    35  		ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal)
    36  	require.NoError(t, err)
    37  
    38  	t.Log("Init a new repo directly into KBFS.")
    39  	dotgitFS, _, err := GetOrCreateRepoAndID(ctx, config, h, "test", "")
    40  	require.NoError(t, err)
    41  
    42  	t.Log("Check that the browser for an uninitialized repo works.")
    43  	b, err := NewBrowser(dotgitFS, config.Clock(), "", sharedCache)
    44  	require.NoError(t, err)
    45  	fis, err := b.ReadDir("")
    46  	require.NoError(t, err)
    47  	require.Len(t, fis, 0)
    48  
    49  	err = rootFS.MkdirAll("worktree", 0600)
    50  	require.NoError(t, err)
    51  	worktreeFS, err := rootFS.Chroot("worktree")
    52  	require.NoError(t, err)
    53  	dotgitStorage, err := NewGitConfigWithoutRemotesStorer(dotgitFS)
    54  	require.NoError(t, err)
    55  	repo, err := gogit.Init(dotgitStorage, worktreeFS)
    56  	require.NoError(t, err)
    57  
    58  	t.Log("Check that the browser for an empty master branch works.")
    59  	b, err = NewBrowser(dotgitFS, config.Clock(), "", sharedCache)
    60  	require.NoError(t, err)
    61  	fis, err = b.ReadDir("")
    62  	require.NoError(t, err)
    63  	require.Len(t, fis, 0)
    64  
    65  	addFileToWorktreeAndCommit(
    66  		ctx, t, config, h, repo, worktreeFS, "foo", "hello")
    67  	addFileToWorktreeAndCommit(
    68  		ctx, t, config, h, repo, worktreeFS, "dir/foo", "olleh")
    69  
    70  	t.Log("Browse the repo and verify the data.")
    71  	b, err = NewBrowser(dotgitFS, config.Clock(), "", sharedCache)
    72  	require.NoError(t, err)
    73  
    74  	if sharedCache != (noopSharedInBrowserCache{}) {
    75  		t.Log("Before anything, cache should be empty")
    76  		_, ok := sharedCache.getFileInfo(b.commitHash, path.Join(b.root, "foo"))
    77  		require.False(t, ok)
    78  	}
    79  
    80  	fi, err := b.Stat("foo")
    81  	require.NoError(t, err)
    82  	require.Equal(t, "foo", fi.Name())
    83  
    84  	if sharedCache != (noopSharedInBrowserCache{}) {
    85  		t.Log("After a Stat call, make sure cache is populated for foo")
    86  		fi, ok := sharedCache.getFileInfo(
    87  			b.commitHash, path.Join(b.root, "foo"))
    88  		require.True(t, ok)
    89  		require.Equal(t, "foo", fi.Name())
    90  	}
    91  
    92  	t.Log("Verify the data in foo.")
    93  	f, err := b.Open("foo")
    94  	require.NoError(t, err)
    95  	defer f.Close()
    96  	data, err := io.ReadAll(f)
    97  	require.NoError(t, err)
    98  	require.Equal(t, "hello", string(data))
    99  
   100  	fis, err = b.ReadDir("dir")
   101  	require.NoError(t, err)
   102  	require.Len(t, fis, 1)
   103  
   104  	if sharedCache != (noopSharedInBrowserCache{}) {
   105  		t.Logf("After a ReadDir, " +
   106  			"make sure cache is populated for dir and dir/foo")
   107  		childrenPaths, ok := sharedCache.getChildrenFileInfos(
   108  			b.commitHash, path.Join(b.root, "dir"))
   109  		require.True(t, ok)
   110  		require.Len(t, childrenPaths, 1)
   111  		require.Equal(t, "foo", childrenPaths[0].Name())
   112  		fi, ok := sharedCache.getFileInfo(
   113  			b.commitHash, path.Join(b.root, "dir", "foo"))
   114  		require.True(t, ok)
   115  		require.Equal(t, "foo", fi.Name())
   116  	}
   117  
   118  	t.Log("Use ReadAt with a small buffer.")
   119  	bf, ok := f.(*browserFile)
   120  	require.True(t, ok)
   121  	bf.maxBufSize = 1
   122  	buf := make([]byte, 3)
   123  	n, err := f.ReadAt(buf, 2)
   124  	require.NoError(t, err)
   125  	require.Equal(t, 3, n)
   126  	require.Equal(t, "llo", string(buf))
   127  
   128  	addSymlink := func(target, link string) {
   129  		err = worktreeFS.Symlink(target, link)
   130  		require.NoError(t, err)
   131  		wt, err := repo.Worktree()
   132  		require.NoError(t, err)
   133  		_, err = wt.Add(link)
   134  		require.NoError(t, err)
   135  		_, err = wt.Commit("sym commit", &gogit.CommitOptions{
   136  			Author: &object.Signature{
   137  				Name:  "me",
   138  				Email: "me@keyba.se",
   139  				When:  time.Now(),
   140  			},
   141  		})
   142  		require.NoError(t, err)
   143  		b, err = NewBrowser(dotgitFS, config.Clock(), "", sharedCache)
   144  		require.NoError(t, err)
   145  		fi, err := b.Lstat(link)
   146  		require.NoError(t, err)
   147  		require.NotZero(t, fi.Mode()&os.ModeSymlink)
   148  		fi, err = b.Stat(link)
   149  		require.NoError(t, err)
   150  		readTarget, err := b.Readlink(link)
   151  		require.NoError(t, err)
   152  		require.Equal(t, target, readTarget)
   153  		require.Zero(t, fi.Mode()&os.ModeSymlink)
   154  		f2, err := b.Open(link)
   155  		require.NoError(t, err)
   156  		defer f2.Close()
   157  		data, err = io.ReadAll(f2)
   158  		require.NoError(t, err)
   159  		require.Equal(t, "hello", string(data))
   160  	}
   161  	t.Log("Add and read a symlink.")
   162  	addSymlink("foo", "symfoo")
   163  
   164  	t.Log("Add and read a second symlink in a chain.")
   165  	err = worktreeFS.MkdirAll("dir", 0700)
   166  	require.NoError(t, err)
   167  	addSymlink("../symfoo", "dir/symfoo")
   168  }
   169  
   170  func TestBrowserNoCache(t *testing.T) {
   171  	testBrowser(t, noopSharedInBrowserCache{})
   172  }
   173  
   174  func TestBrowserWithCache(t *testing.T) {
   175  	cache, err := newLRUSharedInBrowserCache()
   176  	require.NoError(t, err)
   177  	testBrowser(t, cache)
   178  }