github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/git/put_get_test.go (about)

     1  // Copyright 2017 Keybase, Inc. All rights reserved. Use of
     2  // this source code is governed by the included BSD license.
     3  
     4  package git
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"testing"
    10  
    11  	"github.com/keybase/client/go/kbfs/tlf"
    12  	"github.com/keybase/client/go/kbtest"
    13  	"github.com/keybase/client/go/libkb"
    14  	"github.com/keybase/client/go/protocol/chat1"
    15  	"github.com/keybase/client/go/protocol/keybase1"
    16  	"github.com/keybase/client/go/teams"
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  func doPut(t *testing.T, g *libkb.GlobalContext, teamName string, repoID string, repoName string) {
    21  	err := PutMetadata(context.Background(), g, keybase1.PutGitMetadataArg{
    22  		Folder: keybase1.FolderHandle{
    23  			Name:       teamName,
    24  			FolderType: keybase1.FolderType_TEAM,
    25  		},
    26  		RepoID: keybase1.RepoID(repoID),
    27  		Metadata: keybase1.GitLocalMetadata{
    28  			RepoName: keybase1.GitRepoName(repoName),
    29  			PushType: keybase1.GitPushType_CREATEREPO,
    30  		},
    31  	})
    32  	require.NoError(t, err)
    33  }
    34  
    35  func TestPutAndGet(t *testing.T) {
    36  	tc := SetupTest(t, "team", 1)
    37  	defer tc.Cleanup()
    38  
    39  	tc2 := SetupTest(t, "team", 1)
    40  	defer tc2.Cleanup()
    41  
    42  	// Note that the length limit for a team name, with the additional suffix
    43  	// below, is 16 characters. We have 5 to play with, including the implicit
    44  	// underscore after the prefix.
    45  	u, err := kbtest.CreateAndSignupFakeUser("t", tc.G)
    46  	require.NoError(t, err)
    47  
    48  	u2, err := kbtest.CreateAndSignupFakeUser("t", tc2.G)
    49  	require.NoError(t, err)
    50  
    51  	// Create two teams, so that we can test filtering by TeamID.
    52  	teamName1 := u.Username + "t1"
    53  	_, err = teams.CreateRootTeam(context.Background(), tc.G, teamName1, keybase1.TeamSettings{})
    54  	require.NoError(t, err)
    55  	_, err = teams.AddMember(context.Background(), tc.G, teamName1, u2.Username, keybase1.TeamRole_READER, nil)
    56  	require.NoError(t, err)
    57  	team1, _, err := tc.G.GetTeamLoader().Load(context.Background(), keybase1.LoadTeamArg{Name: teamName1})
    58  	require.NoError(t, err)
    59  
    60  	teamName2 := u.Username + "t2"
    61  	_, err = teams.CreateRootTeam(context.Background(), tc.G, teamName2, keybase1.TeamSettings{})
    62  	require.NoError(t, err)
    63  
    64  	// Create two git repos, one in each team. Remember that all we're
    65  	// "creating" here is metadata.
    66  	doPut(t, tc.G, teamName1, "abc123", "repoNameFirst")
    67  	doPut(t, tc.G, teamName2, "def456", "repoNameSecond")
    68  	expectedIDNames := map[string]string{
    69  		"abc123": "repoNameFirst",
    70  		"def456": "repoNameSecond",
    71  	}
    72  
    73  	// Get all repos, and make sure both come back.
    74  	allRepos, err := GetAllMetadata(context.Background(), tc.G)
    75  	require.NoError(t, err)
    76  	require.Equal(t, 2, len(allRepos), "expected to get both repos back, found: %d", len(allRepos))
    77  	for _, repoRes := range allRepos {
    78  		repo, err := repoRes.GetIfOk()
    79  		require.NoError(t, err)
    80  		require.Equal(t, expectedIDNames[string(repo.RepoID)], string(repo.LocalMetadata.RepoName))
    81  		require.Equal(t, repo.Folder.FolderType, keybase1.FolderType_TEAM)
    82  		require.Equal(t, repo.ServerMetadata.LastModifyingUsername, u.Username)
    83  		require.Equal(t, repo.CanDelete, true)
    84  	}
    85  
    86  	// Now get the repos for just one team. Should be only one of the two we just created.
    87  	oneRepoList, err := GetMetadata(context.Background(), tc.G, keybase1.FolderHandle{
    88  		Name:       teamName1,
    89  		FolderType: keybase1.FolderType_TEAM,
    90  	})
    91  	require.NoError(t, err)
    92  	require.Equal(t, 1, len(oneRepoList), "expected to get only one repo back, found: %d", len(oneRepoList))
    93  	oneRepo, err := oneRepoList[0].GetIfOk()
    94  	require.NoError(t, err)
    95  	require.Equal(t, "repoNameFirst", string(oneRepo.LocalMetadata.RepoName))
    96  	require.Equal(t, kbtest.DefaultDeviceName, oneRepo.ServerMetadata.LastModifyingDeviceName)
    97  	require.Equal(t, string(team1.Chain.Id+"_abc123"), oneRepo.GlobalUniqueID)
    98  	require.Equal(t, "keybase://team/"+teamName1+"/repoNameFirst", oneRepo.RepoUrl)
    99  	require.Equal(t, oneRepo.CanDelete, true)
   100  
   101  	// check that the chat system messages were sent for the two doPut calls above
   102  	msgs := kbtest.MockSentMessages(tc.G, tc.T)
   103  	require.Len(t, msgs, 2)
   104  	require.Equal(t, msgs[0].MsgType, chat1.MessageType_SYSTEM)
   105  	require.Equal(t, msgs[1].MsgType, chat1.MessageType_SYSTEM)
   106  	require.Equal(t, msgs[0].Body.System().Gitpush().RepoName, "repoNameFirst")
   107  	require.Equal(t, msgs[1].Body.System().Gitpush().RepoName, "repoNameSecond")
   108  
   109  	t.Logf("reset first user")
   110  	ResetAccountAndLogout(tc, u)
   111  
   112  	t.Logf("we can still read metadata last posted by reset user")
   113  	oneRepoList, err = GetMetadata(context.Background(), tc2.G, keybase1.FolderHandle{
   114  		Name:       teamName1,
   115  		FolderType: keybase1.FolderType_TEAM,
   116  	})
   117  	require.NoError(t, err)
   118  	require.Equal(t, 1, len(oneRepoList), "expected to get only one repo back, found: %d", len(oneRepoList))
   119  	oneRepo, err = oneRepoList[0].GetIfOk()
   120  	require.NoError(t, err)
   121  	require.Equal(t, "repoNameFirst", string(oneRepo.LocalMetadata.RepoName))
   122  	require.Equal(t, kbtest.DefaultDeviceName, oneRepo.ServerMetadata.LastModifyingDeviceName)
   123  	require.Equal(t, string(team1.Chain.Id+"_abc123"), oneRepo.GlobalUniqueID)
   124  	require.Equal(t, "keybase://team/"+teamName1+"/repoNameFirst", oneRepo.RepoUrl)
   125  	require.Equal(t, oneRepo.CanDelete, false)
   126  }
   127  
   128  func ResetAccountAndLogout(tc libkb.TestContext, u *kbtest.FakeUser) {
   129  	err := libkb.ResetAccount(libkb.NewMetaContextForTest(tc), u.NormalizedUsername(), u.Passphrase)
   130  	require.NoError(tc.T, err)
   131  	err = tc.Logout()
   132  	require.NoError(tc.T, err)
   133  }
   134  
   135  func TestDeleteRepo(t *testing.T) {
   136  	tc := SetupTest(t, "team", 1)
   137  	defer tc.Cleanup()
   138  
   139  	// Note that the length limit for a team name, with the additional suffix
   140  	// below, is 16 characters. We have 5 to play with, including the implicit
   141  	// underscore after the prefix.
   142  	u, err := kbtest.CreateAndSignupFakeUser("t", tc.G)
   143  	require.NoError(t, err)
   144  
   145  	// Create a personal repo for this user. (This also exercises the
   146  	// personal-repos-create-an-implicit-team flow.)
   147  	repoName := keybase1.GitRepoName("testRepo123")
   148  	folder := keybase1.FolderHandle{
   149  		Name:       u.Username,
   150  		FolderType: keybase1.FolderType_PRIVATE,
   151  	}
   152  	err = PutMetadata(context.Background(), tc.G, keybase1.PutGitMetadataArg{
   153  		Folder: folder,
   154  		RepoID: keybase1.RepoID("abc123"),
   155  		Metadata: keybase1.GitLocalMetadata{
   156  			RepoName: repoName,
   157  		},
   158  	})
   159  	require.NoError(t, err)
   160  
   161  	// Get all repos, and make sure we see that one.
   162  	allRepos, err := GetAllMetadata(context.Background(), tc.G)
   163  	require.NoError(t, err)
   164  	require.Equal(t, 1, len(allRepos), "expected to see 1 git repo, saw %d", len(allRepos))
   165  	firstRepo, err := allRepos[0].GetIfOk()
   166  	require.NoError(t, err)
   167  	require.Equal(t, repoName, firstRepo.LocalMetadata.RepoName)
   168  
   169  	// Now delete that repo.
   170  	err = DeleteMetadata(context.Background(), tc.G, folder, repoName)
   171  	require.NoError(t, err)
   172  
   173  	// Finally, get all repos again, and make sure it's gone.
   174  	allRepos, err = GetAllMetadata(context.Background(), tc.G)
   175  	require.NoError(t, err)
   176  	require.Equal(t, 0, len(allRepos), "expected the repo to get deleted")
   177  }
   178  
   179  func TestPutAndGetImplicitTeam(t *testing.T) {
   180  	testPutAndGetImplicitTeam(t, false)
   181  	testPutAndGetImplicitTeam(t, true)
   182  }
   183  func testPutAndGetImplicitTeam(t *testing.T, public bool) {
   184  	t.Logf("running with public:%v", public)
   185  
   186  	folderType := keybase1.FolderType_PRIVATE
   187  	if public {
   188  		folderType = keybase1.FolderType_PUBLIC
   189  	}
   190  
   191  	publicnessStr := "private"
   192  	if public {
   193  		publicnessStr = "public"
   194  	}
   195  
   196  	tc := SetupTest(t, "team", 1)
   197  	defer tc.Cleanup()
   198  
   199  	t.Logf("signup users")
   200  	u1, err := kbtest.CreateAndSignupFakeUser("t", tc.G)
   201  	require.NoError(t, err)
   202  	u2, err := kbtest.CreateAndSignupFakeUser("t", tc.G)
   203  	require.NoError(t, err)
   204  
   205  	// Create two repos, one in u2's personal directory (since we're signed in
   206  	// as u2 at this point), and one in the directory that u1 and u2 share. One
   207  	// of the things we'll be testing is that the second one does *not* show up
   208  	// in the results of GetAllMetadata. This is a product choice -- we want to
   209  	// pretend they kinda don't exist.
   210  
   211  	t.Logf("me only repo")
   212  	repoName1 := keybase1.GitRepoName("me only repo")
   213  	testFolder1 := keybase1.FolderHandle{
   214  		Name:       u2.Username,
   215  		FolderType: folderType,
   216  	}
   217  	err = PutMetadata(context.TODO(), tc.G, keybase1.PutGitMetadataArg{
   218  		Folder: testFolder1,
   219  		RepoID: keybase1.RepoID("abc123"),
   220  		Metadata: keybase1.GitLocalMetadata{
   221  			RepoName: repoName1,
   222  		},
   223  	})
   224  	require.NoError(t, err)
   225  
   226  	t.Logf("second repo")
   227  	repoName2 := keybase1.GitRepoName(fmt.Sprintf("two person %s repo", publicnessStr))
   228  	normalizedTLFName, err := tlf.NormalizeNamesInTLF(libkb.NewMetaContextForTest(tc), []string{u1.Username, u2.Username}, nil, "")
   229  	require.NoError(t, err)
   230  	testFolder2 := keybase1.FolderHandle{
   231  		Name:       normalizedTLFName,
   232  		FolderType: folderType,
   233  	}
   234  	err = PutMetadata(context.Background(), tc.G, keybase1.PutGitMetadataArg{
   235  		Folder: testFolder2,
   236  		RepoID: keybase1.RepoID("abc123"),
   237  		Metadata: keybase1.GitLocalMetadata{
   238  			RepoName: repoName2,
   239  		},
   240  	})
   241  	require.NoError(t, err)
   242  
   243  	// Now make sure we can query these repos (or not, as appropriate for the
   244  	// multi-person case).
   245  
   246  	assertStuffAboutRepo := func(t *testing.T, repoRes keybase1.GitRepoResult, folder keybase1.FolderHandle, repoName keybase1.GitRepoName) {
   247  		repo, err := repoRes.GetIfOk()
   248  		require.NoError(t, err)
   249  		require.Equal(t, repoName, repo.LocalMetadata.RepoName)
   250  		require.Equal(t, folderType, repo.Folder.FolderType)
   251  		require.Equal(t, kbtest.DefaultDeviceName, repo.ServerMetadata.LastModifyingDeviceName)
   252  		require.Equal(t, "keybase://"+publicnessStr+"/"+folder.Name+"/"+string(repoName), repo.RepoUrl)
   253  		require.Equal(t, repo.CanDelete, true)
   254  	}
   255  
   256  	t.Logf("assertions")
   257  	// Test fetching the repo with GetMetadata.
   258  	oneRepo, err := GetMetadata(context.Background(), tc.G, testFolder1)
   259  	require.NoError(t, err)
   260  	require.Equal(t, 1, len(oneRepo))
   261  	assertStuffAboutRepo(t, oneRepo[0], testFolder1, repoName1)
   262  
   263  	// Also test fetching the 2-person repo with GetMetadata. This
   264  	// *should* work, even though it's hidden from GetAllMetadata.
   265  	oneRepo, err = GetMetadata(context.Background(), tc.G, testFolder2)
   266  	require.NoError(t, err)
   267  	require.Equal(t, 1, len(oneRepo))
   268  	assertStuffAboutRepo(t, oneRepo[0], testFolder2, repoName2)
   269  
   270  	// Finally test the GetAllMetadata results. This should *not* see the
   271  	// 2-person repo.
   272  	allRepos, err := GetAllMetadata(context.Background(), tc.G)
   273  	require.NoError(t, err)
   274  	require.Equal(t, 1, len(allRepos), "the two-person repo should be hidden!")
   275  	assertStuffAboutRepo(t, allRepos[0], testFolder1, repoName1)
   276  }
   277  
   278  func TestPutAndGetWritersCantDelete(t *testing.T) {
   279  	tc := SetupTest(t, "team", 1)
   280  	defer tc.Cleanup()
   281  
   282  	u1, err := kbtest.CreateAndSignupFakeUser("t", tc.G)
   283  	require.NoError(t, err)
   284  	u2, err := kbtest.CreateAndSignupFakeUser("t", tc.G)
   285  	require.NoError(t, err)
   286  
   287  	// u2 creates a team where u1 is just a writer
   288  	teamName := u2.Username + "t1"
   289  	_, err = teams.CreateRootTeam(context.Background(), tc.G, teamName, keybase1.TeamSettings{})
   290  	require.NoError(t, err)
   291  	_, err = teams.AddMember(context.Background(), tc.G, teamName, u1.Username, keybase1.TeamRole_WRITER, nil)
   292  	if err != nil {
   293  		t.Fatal(err)
   294  	}
   295  
   296  	// Create a git repo for that team
   297  	doPut(t, tc.G, teamName, "abc123", "dummyRepoName")
   298  
   299  	// Load the repo and confirm that u2 sees it as CanDelete=true.
   300  	repos, err := GetAllMetadata(context.Background(), tc.G)
   301  	require.NoError(t, err)
   302  	require.Equal(t, 1, len(repos), "expected exactly one repo")
   303  	firstRepo, err := repos[0].GetIfOk()
   304  	require.NoError(t, err)
   305  	require.Equal(t, true, firstRepo.CanDelete, "owners/admins should be able to delete")
   306  
   307  	// Now log in as u1, load the repo again, and confirm that u1 sees it as CanDelete=FALSE.
   308  	err = tc.Logout()
   309  	require.NoError(t, err)
   310  	err = u1.Login(tc.G)
   311  	require.NoError(t, err)
   312  	repos2, err := GetAllMetadata(context.Background(), tc.G)
   313  	require.NoError(t, err)
   314  	require.Equal(t, 1, len(repos2), "expected exactly one repo")
   315  	repo2, err := repos2[0].GetIfOk()
   316  	require.NoError(t, err)
   317  	require.Equal(t, false, repo2.CanDelete, "non-admins must not be able to delete")
   318  }