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 }