github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/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  }