github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/libgit/autogit_node_wrappers_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 "fmt" 9 "io" 10 "os" 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/pkg/errors" 21 "github.com/stretchr/testify/require" 22 gogit "gopkg.in/src-d/go-git.v4" 23 "gopkg.in/src-d/go-git.v4/plumbing/object" 24 ) 25 26 func TestAutogitNodeWrappersNoRepos(t *testing.T) { 27 ctx, config, cancel, tempdir := initConfigForAutogit(t) 28 defer cancel() 29 defer os.RemoveAll(tempdir) 30 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 31 32 shutdown := StartAutogit(config, 25) 33 defer shutdown() 34 35 h, err := tlfhandle.ParseHandle( 36 ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private) 37 require.NoError(t, err) 38 rootFS, err := libfs.NewFS( 39 ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal) 40 require.NoError(t, err) 41 42 t.Log("Looking at user1's autogit directory should fail if no git repos") 43 _, err = rootFS.ReadDir(AutogitRoot) 44 require.Error(t, err) 45 } 46 47 func checkAutogitOneFile(t *testing.T, rootFS *libfs.FS) { 48 fis, err := rootFS.ReadDir(".kbfs_autogit/test") 49 require.NoError(t, err) 50 require.Len(t, fis, 1) 51 f, err := rootFS.Open(".kbfs_autogit/test/foo") 52 require.NoError(t, err) 53 defer f.Close() 54 data, err := io.ReadAll(f) 55 require.NoError(t, err) 56 require.Equal(t, "hello", string(data)) 57 } 58 59 func checkAutogitTwoFiles(t *testing.T, rootFS *libfs.FS) { 60 fis, err := rootFS.ReadDir(".kbfs_autogit/test") 61 require.NoError(t, err) 62 require.Len(t, fis, 2) // foo and foo2 63 f, err := rootFS.Open(".kbfs_autogit/test/foo") 64 require.NoError(t, err) 65 defer f.Close() 66 data, err := io.ReadAll(f) 67 require.NoError(t, err) 68 require.Equal(t, "hello", string(data)) 69 f2, err := rootFS.Open(".kbfs_autogit/test/foo2") 70 require.NoError(t, err) 71 defer f2.Close() 72 data2, err := io.ReadAll(f2) 73 require.NoError(t, err) 74 require.Equal(t, "hello2", string(data2)) 75 // Make sure a non-existent file gives the right error. 76 _, err = rootFS.Open(".kbfs_autogit/test/missing") 77 require.True(t, os.IsNotExist(errors.Cause(err))) 78 } 79 80 func TestAutogitRepoNode(t *testing.T) { 81 ctx, config, cancel, tempdir := initConfigForAutogit(t) 82 defer cancel() 83 defer os.RemoveAll(tempdir) 84 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 85 86 am := NewAutogitManager(config, 25) 87 defer am.Shutdown() 88 rw := rootWrapper{am} 89 config.AddRootNodeWrapper(rw.wrap) 90 91 h, err := tlfhandle.ParseHandle( 92 ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private) 93 require.NoError(t, err) 94 rootFS, err := libfs.NewFS( 95 ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal) 96 require.NoError(t, err) 97 98 t.Log("Init a new repo directly into KBFS.") 99 dotgitFS, _, err := GetOrCreateRepoAndID(ctx, config, h, "test", "") 100 require.NoError(t, err) 101 err = rootFS.MkdirAll("worktree", 0600) 102 require.NoError(t, err) 103 worktreeFS, err := rootFS.Chroot("worktree") 104 require.NoError(t, err) 105 dotgitStorage, err := NewGitConfigWithoutRemotesStorer(dotgitFS) 106 require.NoError(t, err) 107 repo, err := gogit.Init(dotgitStorage, worktreeFS) 108 require.NoError(t, err) 109 addFileToWorktreeAndCommit( 110 ctx, t, config, h, repo, worktreeFS, "foo", "hello") 111 112 t.Log("Use autogit to clone it using ReadDir") 113 checkAutogitOneFile(t, rootFS) 114 115 t.Log("Update the source repo and make sure the autogit repos update too") 116 addFileToWorktreeAndCommit( 117 ctx, t, config, h, repo, worktreeFS, "foo2", "hello2") 118 119 t.Log("Force the source repo to update for the user") 120 srcRootNode, _, err := config.KBFSOps().GetOrCreateRootNode( 121 ctx, h, data.MasterBranch) 122 require.NoError(t, err) 123 err = config.KBFSOps().SyncFromServer( 124 ctx, srcRootNode.GetFolderBranch(), nil) 125 require.NoError(t, err) 126 127 t.Log("Update the dest repo") 128 dstRootNode, _, err := config.KBFSOps().GetOrCreateRootNode( 129 ctx, h, data.MasterBranch) 130 require.NoError(t, err) 131 err = config.KBFSOps().SyncFromServer( 132 ctx, dstRootNode.GetFolderBranch(), nil) 133 require.NoError(t, err) 134 135 checkAutogitTwoFiles(t, rootFS) 136 137 t.Log("Switch to branch, check in more files.") 138 wt, err := repo.Worktree() 139 require.NoError(t, err) 140 err = wt.Checkout(&gogit.CheckoutOptions{ 141 Branch: "refs/heads/dir/test-branch", 142 Create: true, 143 }) 144 require.NoError(t, err) 145 addFileToWorktreeAndCommit( 146 ctx, t, config, h, repo, worktreeFS, "foo3", "hello3") 147 err = wt.Checkout(&gogit.CheckoutOptions{Branch: "refs/heads/master"}) 148 require.NoError(t, err) 149 checkAutogitTwoFiles(t, rootFS) 150 151 t.Logf("Check the third file that's only on the branch") 152 f3, err := rootFS.Open( 153 ".kbfs_autogit/test/.kbfs_autogit_branch_dir/" + 154 ".kbfs_autogit_branch_test-branch/foo3") 155 require.NoError(t, err) 156 defer f3.Close() 157 data3, err := io.ReadAll(f3) 158 require.NoError(t, err) 159 require.Equal(t, "hello3", string(data3)) 160 161 t.Logf("Use colons instead of slashes in the branch name") 162 f4, err := rootFS.Open( 163 ".kbfs_autogit/test/.kbfs_autogit_branch_dir^test-branch/foo3") 164 require.NoError(t, err) 165 defer f4.Close() 166 data4, err := io.ReadAll(f4) 167 require.NoError(t, err) 168 require.Equal(t, "hello3", string(data4)) 169 170 t.Logf("Check non-normalized repo name") 171 f5, err := rootFS.Open(".kbfs_autogit/test.git/foo") 172 require.NoError(t, err) 173 defer f5.Close() 174 data5, err := io.ReadAll(f5) 175 require.NoError(t, err) 176 require.Equal(t, "hello", string(data5)) 177 178 err = dotgitFS.SyncAll() 179 require.NoError(t, err) 180 } 181 182 func TestAutogitRepoNodeReadonly(t *testing.T) { 183 ctx, config, cancel, tempdir := initConfigForAutogit(t) 184 defer cancel() 185 defer os.RemoveAll(tempdir) 186 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 187 188 am := NewAutogitManager(config, 25) 189 defer am.Shutdown() 190 rw := rootWrapper{am} 191 config.AddRootNodeWrapper(rw.wrap) 192 193 h, err := tlfhandle.ParseHandle( 194 ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Public) 195 require.NoError(t, err) 196 rootFS, err := libfs.NewFS( 197 ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal) 198 require.NoError(t, err) 199 200 t.Log("Init a new repo directly into KBFS.") 201 dotgitFS, _, err := GetOrCreateRepoAndID(ctx, config, h, "test", "") 202 require.NoError(t, err) 203 err = rootFS.MkdirAll("worktree", 0600) 204 require.NoError(t, err) 205 worktreeFS, err := rootFS.Chroot("worktree") 206 require.NoError(t, err) 207 dotgitStorage, err := NewGitConfigWithoutRemotesStorer(dotgitFS) 208 require.NoError(t, err) 209 repo, err := gogit.Init(dotgitStorage, worktreeFS) 210 require.NoError(t, err) 211 addFileToWorktreeAndCommit( 212 ctx, t, config, h, repo, worktreeFS, "foo", "hello") 213 214 t.Log("Use autogit to open it as another user.") 215 config2 := libkbfs.ConfigAsUser(config, "user2") 216 defer libkbfs.CheckConfigAndShutdown(ctx, t, config2) 217 am2 := NewAutogitManager(config2, 25) 218 defer am2.Shutdown() 219 rw2 := rootWrapper{am2} 220 config2.AddRootNodeWrapper(rw2.wrap) 221 rootFS2, err := libfs.NewFS( 222 ctx, config2, h, data.MasterBranch, "", "", 223 keybase1.MDPriorityNormal) 224 require.NoError(t, err) 225 checkAutogitOneFile(t, rootFS2) 226 227 addFileToWorktree(t, repo, worktreeFS, "foo2", "hello2") 228 t.Log("Repacking objects to more closely resemble a real kbfsgit push, " + 229 "which only creates packfiles") 230 err = repo.RepackObjects(&gogit.RepackConfig{}) 231 require.NoError(t, err) 232 objFS, err := dotgitFS.Chroot("objects") 233 require.NoError(t, err) 234 fis, err := objFS.ReadDir("/") 235 require.NoError(t, err) 236 for _, fi := range fis { 237 if fi.Name() != "pack" { 238 err = libfs.RecursiveDelete(ctx, objFS.(*libfs.FS), fi) 239 require.NoError(t, err) 240 } 241 } 242 t.Log("Repacking done") 243 commitWorktree(ctx, t, config, h, worktreeFS) 244 245 t.Log("Force the source repo to update for the second user") 246 srcRootNode2, _, err := config2.KBFSOps().GetOrCreateRootNode( 247 ctx, h, data.MasterBranch) 248 require.NoError(t, err) 249 err = config2.KBFSOps().SyncFromServer( 250 ctx, srcRootNode2.GetFolderBranch(), nil) 251 require.NoError(t, err) 252 253 t.Log("Update the dest repo") 254 dstRootNode2, _, err := config2.KBFSOps().GetOrCreateRootNode( 255 ctx, h, data.MasterBranch) 256 require.NoError(t, err) 257 err = config2.KBFSOps().SyncFromServer( 258 ctx, dstRootNode2.GetFolderBranch(), nil) 259 require.NoError(t, err) 260 261 checkAutogitTwoFiles(t, rootFS2) 262 } 263 264 func TestAutogitCommitFile(t *testing.T) { 265 ctx, config, cancel, tempdir := initConfigForAutogit(t) 266 defer cancel() 267 defer os.RemoveAll(tempdir) 268 defer libkbfs.CheckConfigAndShutdown(ctx, t, config) 269 270 am := NewAutogitManager(config, 25) 271 defer am.Shutdown() 272 rw := rootWrapper{am} 273 config.AddRootNodeWrapper(rw.wrap) 274 275 h, err := tlfhandle.ParseHandle( 276 ctx, config.KBPKI(), config.MDOps(), nil, "user1", tlf.Private) 277 require.NoError(t, err) 278 rootFS, err := libfs.NewFS( 279 ctx, config, h, data.MasterBranch, "", "", keybase1.MDPriorityNormal) 280 require.NoError(t, err) 281 282 t.Log("Init a new repo directly into KBFS.") 283 dotgitFS, _, err := GetOrCreateRepoAndID(ctx, config, h, "test", "") 284 require.NoError(t, err) 285 err = rootFS.MkdirAll("worktree", 0600) 286 require.NoError(t, err) 287 worktreeFS, err := rootFS.Chroot("worktree") 288 require.NoError(t, err) 289 dotgitStorage, err := NewGitConfigWithoutRemotesStorer(dotgitFS) 290 require.NoError(t, err) 291 repo, err := gogit.Init(dotgitStorage, worktreeFS) 292 require.NoError(t, err) 293 294 msg1 := "commit1" 295 user1 := "user1" 296 email1 := "user1@keyba.se" 297 time1 := time.Now() 298 hash1 := addFileToWorktreeWithInfo( 299 t, repo, worktreeFS, "foo", "hello\n\nworld\n", msg1, user1, email1, time1) 300 commitWorktree(ctx, t, config, h, worktreeFS) 301 302 t.Log("Check the first commit -- no diff") 303 headerFormat := "commit %s\nAuthor: %s <%s>\nDate: %s\n\n %s\n" 304 expectedCommit1 := fmt.Sprintf( 305 headerFormat, hash1.String(), user1, email1, 306 time1.Format(object.DateFormat), msg1) 307 308 f1, err := rootFS.Open( 309 ".kbfs_autogit/test.git/" + AutogitCommitPrefix + hash1.String()) 310 require.NoError(t, err) 311 defer f1.Close() 312 data1, err := io.ReadAll(f1) 313 require.NoError(t, err) 314 require.Equal(t, expectedCommit1, string(data1)) 315 316 t.Log("Make and check a new commit") 317 msg2 := "commit2" 318 user2 := "user2" 319 email2 := "user2@keyba.se" 320 time2 := time1.Add(1 * time.Minute) 321 hash2 := addFileToWorktreeWithInfo( 322 t, repo, worktreeFS, "foo", "hello\n\nworld\nhello world\n", msg2, user2, email2, time2) 323 commitWorktree(ctx, t, config, h, worktreeFS) 324 325 commit1, err := repo.CommitObject(hash1) 326 require.NoError(t, err) 327 tree1, err := commit1.Tree() 328 require.NoError(t, err) 329 commit2, err := repo.CommitObject(hash2) 330 require.NoError(t, err) 331 tree2, err := commit2.Tree() 332 require.NoError(t, err) 333 334 entry1, err := tree1.FindEntry("foo") 335 require.NoError(t, err) 336 entry2, err := tree2.FindEntry("foo") 337 require.NoError(t, err) 338 339 expectedCommit2 := fmt.Sprintf( 340 headerFormat, hash2.String(), user2, email2, 341 time2.Format(object.DateFormat), msg2) + 342 fmt.Sprintf(`diff --git a/foo b/foo 343 index %s..%s 100644 344 --- a/foo 345 +++ b/foo 346 @@ -1,3 +1,4 @@ 347 hello 348 349 world 350 +hello world 351 `, entry1.Hash.String(), entry2.Hash.String()) 352 f2, err := rootFS.Open( 353 ".kbfs_autogit/test.git/" + AutogitCommitPrefix + hash2.String()) 354 require.NoError(t, err) 355 defer f2.Close() 356 data2, err := io.ReadAll(f2) 357 require.NoError(t, err) 358 require.Equal(t, expectedCommit2, string(data2)) 359 }