github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/systests/team_list_test.go (about) 1 package systests 2 3 import ( 4 "testing" 5 6 "github.com/keybase/client/go/libkb" 7 keybase1 "github.com/keybase/client/go/protocol/keybase1" 8 "github.com/keybase/client/go/teams" 9 "github.com/stretchr/testify/require" 10 "golang.org/x/net/context" 11 ) 12 13 func findMember(user *smuUser, members []keybase1.TeamMemberDetails) *keybase1.TeamMemberDetails { 14 for _, member := range members { 15 if member.Username == user.username { 16 return &member 17 } 18 } 19 return nil 20 } 21 22 func TestTeamList(t *testing.T) { 23 ctx := newSMUContext(t) 24 defer ctx.cleanup() 25 26 // Step 1 - create the initial team with mix of normal members, 27 // reset members, pukless users, social invites etc. 28 29 ann := ctx.installKeybaseForUser("ann", 10) 30 ann.signup() 31 t.Logf("Signed up ann (%s)", ann.username) 32 33 bob := ctx.installKeybaseForUser("bob", 10) 34 bob.signup() 35 t.Logf("Signed up bob (%s)", bob.username) 36 37 pam := ctx.installKeybaseForUser("pam", 10) 38 pam.signup() 39 t.Logf("Signed up pam (%s)", pam.username) 40 41 john := ctx.installKeybaseForUser("john", 10) 42 john.signupNoPUK() 43 t.Logf("Signed up PUK-less user john (%s)", john.username) 44 45 ed := ctx.installKeybaseForUser("ed", 10) 46 ed.signup() 47 ed.reset() 48 ed.loginAfterResetNoPUK(10) 49 t.Logf("Signed up ed (%s), reset, and reprovisioned without PUK", ed.username) 50 51 team := ann.createTeam([]*smuUser{bob, pam}) 52 t.Logf("Team created (%s)", team.name) 53 54 pam.reset() 55 t.Logf("Pam resets (%s)", pam.username) 56 57 ann.addWriter(team, john) 58 t.Logf("Adding john (%s)", john.username) 59 60 ann.addWriter(team, ed) 61 t.Logf("Adding ed (%s)", ed.username) 62 63 teamCli := ann.getTeamsClient() 64 65 ann.setUIDMapperNoCachingMode(true) 66 67 rootername := randomUser("arbitrary").username 68 _, err := teamCli.TeamAddMember(context.TODO(), keybase1.TeamAddMemberArg{ 69 TeamID: team.ID, 70 Username: rootername + "@rooter", 71 Role: keybase1.TeamRole_WRITER, 72 }) 73 require.NoError(t, err) 74 75 t.Logf("Added rooter (%s@rooter)", rootername) 76 77 // Examine results from TeamGet 78 79 details, err := teamCli.TeamGet(context.TODO(), keybase1.TeamGetArg{ 80 Name: team.name, 81 }) 82 require.NoError(t, err) 83 84 require.Equal(t, 1, len(details.Members.Owners)) 85 require.Equal(t, 0, len(details.Members.Admins)) 86 require.Equal(t, 4, len(details.Members.Writers)) 87 require.Equal(t, 0, len(details.Members.Readers)) 88 require.Equal(t, 0, len(details.Members.Bots)) 89 require.Equal(t, 0, len(details.Members.RestrictedBots)) 90 91 annMember := findMember(ann, details.Members.Owners) 92 require.NotNil(t, annMember) 93 require.True(t, annMember.Status.IsActive()) 94 require.False(t, annMember.NeedsPUK) 95 96 bobMember := findMember(bob, details.Members.Writers) 97 require.NotNil(t, bobMember) 98 require.True(t, bobMember.Status.IsActive()) 99 require.False(t, bobMember.NeedsPUK) 100 101 pamMember := findMember(pam, details.Members.Writers) 102 require.NotNil(t, pamMember) 103 require.True(t, pamMember.Status.IsReset()) 104 require.False(t, pamMember.NeedsPUK) 105 106 johnMember := findMember(john, details.Members.Writers) 107 require.NotNil(t, johnMember) 108 require.True(t, johnMember.Status.IsActive()) 109 require.True(t, johnMember.NeedsPUK) 110 111 edMember := findMember(ed, details.Members.Writers) 112 require.NotNil(t, edMember) 113 require.True(t, edMember.Status.IsActive()) 114 require.True(t, edMember.NeedsPUK) 115 116 require.Equal(t, 1, len(details.AnnotatedActiveInvites)) 117 for _, invite := range details.AnnotatedActiveInvites { 118 // There should be only one invite 119 require.EqualValues(t, rootername, invite.InviteMetadata.Invite.Name) 120 } 121 122 // Examine results from TeamList (mostly MemberCount) 123 124 check := func(list *keybase1.AnnotatedTeamList) { 125 require.Equal(t, 1, len(list.Teams)) 126 127 teamInfo := list.Teams[0] 128 require.Equal(t, team.name, teamInfo.FqName) 129 require.Equal(t, 5, teamInfo.MemberCount) 130 } 131 132 list, err := teamCli.TeamListVerified(context.TODO(), keybase1.TeamListVerifiedArg{}) 133 require.NoError(t, err) 134 135 check(&list) 136 137 list, err = teamCli.TeamListUnverified(context.TODO(), keybase1.TeamListUnverifiedArg{}) 138 require.NoError(t, err) 139 140 check(&list) 141 } 142 143 func TestTeamListOpenTeamFilter(t *testing.T) { 144 // Open teams filter out inactive members to the rpc. 145 tt := newTeamTester(t) 146 defer tt.cleanup() 147 148 standaloneArgs := standaloneUserArgs{ 149 disableGregor: true, 150 suppressTeamChatAnnounce: true, 151 } 152 153 ann := makeUserStandalone(t, tt, "ann", standaloneArgs) 154 bob := makeUserStandalone(t, tt, "bob", standaloneArgs) 155 tom := makeUserStandalone(t, tt, "tom", standaloneArgs) 156 157 id, teamName := ann.createTeam2() 158 t.Logf("Team created %q", teamName) 159 ann.teamSetSettings(id, keybase1.TeamSettings{ 160 Open: true, 161 JoinAs: keybase1.TeamRole_WRITER, 162 }) 163 164 ann.addTeamMember(teamName.String(), bob.username, keybase1.TeamRole_ADMIN) 165 ann.addTeamMember(teamName.String(), tom.username, keybase1.TeamRole_WRITER) 166 ann.tc.G.UIDMapper.SetTestingNoCachingMode(true) 167 168 bob.reset() 169 tom.reset() 170 171 details, err := ann.teamsClient.TeamGetByID(context.Background(), keybase1.TeamGetByIDArg{ 172 Id: id, 173 }) 174 require.NoError(t, err) 175 176 require.Len(t, details.Members.Owners, 1) 177 require.Len(t, details.Members.Admins, 1) 178 // Reset writer is filtered out because it's an open team. 179 require.Len(t, details.Members.Writers, 0) 180 } 181 182 func TestTeamListOpenTeams(t *testing.T) { 183 tt := newTeamTester(t) 184 defer tt.cleanup() 185 186 ann := tt.addUser("ann") 187 t.Logf("Signed up ann (%s)", ann.username) 188 189 id1, team1 := ann.createTeam2() 190 t.Logf("Team 1 created (%s)", team1) 191 192 id2, team2 := ann.createTeam2() 193 t.Logf("Team 2 created (%s)", team2) 194 195 ann.teamSetSettings(id2, keybase1.TeamSettings{ 196 Open: true, 197 JoinAs: keybase1.TeamRole_WRITER, 198 }) 199 200 check := func(list *keybase1.AnnotatedTeamList) { 201 require.Equal(t, 2, len(list.Teams)) 202 for _, teamInfo := range list.Teams { 203 if teamInfo.TeamID == id1 { 204 require.False(t, teamInfo.IsOpenTeam) 205 } else if teamInfo.TeamID == id2 { 206 require.True(t, teamInfo.IsOpenTeam) 207 } else { 208 t.Fatalf("Unexpected team name %v", teamInfo) 209 } 210 211 require.Equal(t, 1, teamInfo.MemberCount) 212 } 213 } 214 215 teamCli := ann.teamsClient 216 217 list, err := teamCli.TeamListVerified(context.Background(), keybase1.TeamListVerifiedArg{}) 218 require.NoError(t, err) 219 220 check(&list) 221 222 list, err = teamCli.TeamListUnverified(context.Background(), keybase1.TeamListUnverifiedArg{}) 223 require.NoError(t, err) 224 225 check(&list) 226 } 227 228 func TestTeamDuplicateUIDList(t *testing.T) { 229 tt := newTeamTester(t) 230 defer tt.cleanup() 231 232 ann := tt.addUser("ann") 233 t.Logf("Signed up ann (%s)", ann.username) 234 235 // We have to disable caching in UIDMapper because after bob 236 // resets and provisions, we have no way to be aware of that, and 237 // we might see cached bob in subsequent teamList calls. 238 ann.tc.G.UIDMapper.SetTestingNoCachingMode(true) 239 240 bob := tt.addPuklessUser("bob") 241 t.Logf("Signed up PUK-less user bob (%s)", bob.username) 242 243 team := ann.createTeam() 244 t.Logf("Team created (%s)", team) 245 246 ann.addTeamMember(team, bob.username, keybase1.TeamRole_WRITER) 247 bob.reset() 248 bob.loginAfterReset() 249 250 t.Logf("Bob (%s) resets and reprovisions", bob.username) 251 252 ann.addTeamMember(team, bob.username, keybase1.TeamRole_WRITER) 253 254 teamCli := ann.teamsClient 255 details, err := teamCli.TeamGet(context.TODO(), keybase1.TeamGetArg{ 256 Name: team, 257 }) 258 require.NoError(t, err) 259 260 // Expecting just the active writer here, and not inactive 261 // (because of reset) invite. 262 require.Equal(t, 1, len(details.Members.Writers)) 263 member := details.Members.Writers[0] 264 require.True(t, member.Status.IsActive()) 265 require.False(t, member.NeedsPUK) 266 267 // Check both functions: slow TeamListVerified, and fast (server 268 // trust) TeamList. 269 270 // TeamList reports memberCount of two: ann and bob. Second bob is 271 // ignored, because memberCount is set to number of unique UIDs. 272 273 check := func(list *keybase1.AnnotatedTeamList) { 274 require.Equal(t, 1, len(list.Teams)) 275 276 teamInfo := list.Teams[0] 277 require.Equal(t, team, teamInfo.FqName) 278 require.Equal(t, 2, teamInfo.MemberCount) 279 } 280 281 t.Logf("Calling TeamListVerified") 282 list, err := teamCli.TeamListVerified(context.TODO(), keybase1.TeamListVerifiedArg{}) 283 require.NoError(t, err) 284 285 check(&list) 286 287 t.Logf("Calling TeamList") 288 list, err = teamCli.TeamListUnverified(context.TODO(), keybase1.TeamListUnverifiedArg{}) 289 require.NoError(t, err) 290 291 check(&list) 292 } 293 294 func setupNestedSubteams(t *testing.T, user *userPlusDevice) (teamNames []keybase1.TeamName) { 295 teamStr := user.createTeam() 296 t.Logf("Team created (%s)", teamStr) 297 298 team, err := keybase1.TeamNameFromString(teamStr) 299 require.NoError(t, err) 300 301 createSubteam := func(parentName keybase1.TeamName, subteamName string) keybase1.TeamName { 302 subteam, err := teams.CreateSubteam(context.Background(), user.tc.G, subteamName, parentName, keybase1.TeamRole_NONE /* addSelfAs */) 303 require.NoError(t, err) 304 subteamObj, err := teams.Load(context.Background(), user.tc.G, keybase1.LoadTeamArg{ID: *subteam}) 305 require.NoError(t, err) 306 return subteamObj.Name() 307 } 308 309 subTeam1 := createSubteam(team, "staff") 310 311 sub1SubTeam1 := createSubteam(subTeam1, "legal") 312 sub1SubTeam2 := createSubteam(subTeam1, "hr") 313 314 subTeam2 := createSubteam(team, "offtopic") 315 316 sub2SubTeam1 := createSubteam(subTeam2, "games") 317 sub2SubTeam2 := createSubteam(subTeam2, "crypto") 318 sub2SubTeam3 := createSubteam(subTeam2, "cryptocurrency") 319 return []keybase1.TeamName{team, subTeam1, subTeam2, sub1SubTeam1, sub1SubTeam2, sub2SubTeam1, sub2SubTeam2, sub2SubTeam3} 320 } 321 322 func TestTeamTree(t *testing.T) { 323 tt := newTeamTester(t) 324 defer tt.cleanup() 325 326 ann := tt.addUser("ann") 327 t.Logf("Signed up ann (%s)", ann.username) 328 329 allNestedTeamNames := setupNestedSubteams(t, ann) 330 331 checkTeamTree := func(teamName keybase1.TeamName, expectedTree ...keybase1.TeamName) { 332 set := make(map[string]bool) 333 for _, v := range expectedTree { 334 set[v.String()] = false 335 } 336 337 tree, err := teams.TeamTreeUnverified(context.Background(), ann.tc.G, keybase1.TeamTreeUnverifiedArg{Name: teamName}) 338 require.NoError(t, err) 339 require.Equal(t, len(expectedTree), len(tree.Entries)) 340 341 for _, entry := range tree.Entries { 342 name := entry.Name.String() 343 alreadyFound, exists := set[name] 344 require.True(t, exists, "Found unexpected team %s in tree of %s", name, teamName) 345 require.False(t, alreadyFound, "Duplicate team %s in tree of %s", name, teamName) 346 set[name] = true 347 } 348 } 349 350 for _, teamOrSubteam := range allNestedTeamNames { 351 // TeamTree always shows the whole tree no matter which subteam it starts from 352 checkTeamTree(teamOrSubteam, allNestedTeamNames...) 353 } 354 } 355 356 func TestTeamGetSubteams(t *testing.T) { 357 tt := newTeamTester(t) 358 defer tt.cleanup() 359 360 bob := tt.addUser("bob") 361 t.Logf("Signed up bob (%s)", bob.username) 362 363 allNestedTeamNames := setupNestedSubteams(t, bob) 364 365 checkTeamSubteams := func(teamName keybase1.TeamName, expectedSubteams []keybase1.TeamName) { 366 set := make(map[string]bool) 367 for _, v := range expectedSubteams { 368 set[v.String()] = false 369 } 370 371 res, err := teams.ListSubteamsUnverified(libkb.NewMetaContext(context.Background(), bob.tc.G), teamName) 372 require.NoError(t, err) 373 require.Equal(t, len(expectedSubteams), len(res.Entries)) 374 375 for _, entry := range res.Entries { 376 name := entry.Name.String() 377 alreadyFound, exists := set[name] 378 require.True(t, exists, "Found unexpected team %s in subteam list of %s", name, teamName) 379 require.False(t, alreadyFound, "Duplicate team %s in subteam list of %s", name, teamName) 380 set[name] = true 381 } 382 } 383 384 checkTeamSubteams(allNestedTeamNames[0], allNestedTeamNames[1:]) 385 checkTeamSubteams(allNestedTeamNames[1], allNestedTeamNames[3:5]) 386 checkTeamSubteams(allNestedTeamNames[2], allNestedTeamNames[5:]) 387 } 388 389 func TestTeamProfileAddList(t *testing.T) { 390 tt := newTeamTester(t) 391 defer tt.cleanup() 392 393 ann := tt.addUser("ann") 394 t.Logf("Signed up ann (%s)", ann.username) 395 396 teamID, teamName := ann.createTeam2() 397 t.Logf("Team created (%s)", teamName) 398 399 res, err := ann.teamsClient.TeamProfileAddList(context.TODO(), keybase1.TeamProfileAddListArg{Username: "t_alice"}) 400 require.NoError(t, err) 401 require.Len(t, res, 1) 402 require.Equal(t, keybase1.TeamProfileAddEntry{ 403 TeamID: teamID, 404 TeamName: teamName, 405 Open: false, 406 DisabledReason: "", 407 }, res[0]) 408 }