github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/subteam_rename_test.go (about)

     1  package chat
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"sort"
     7  	"testing"
     8  	"time"
     9  
    10  	"github.com/keybase/client/go/chat/types"
    11  	"github.com/keybase/client/go/chat/utils"
    12  	"github.com/keybase/client/go/protocol/chat1"
    13  	"github.com/keybase/client/go/protocol/keybase1"
    14  	"github.com/keybase/client/go/teams"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  func TestChatSubteamRename(t *testing.T) {
    19  	runWithMemberTypes(t, func(mt chat1.ConversationMembersType) {
    20  		switch mt {
    21  		case chat1.ConversationMembersType_TEAM:
    22  		default:
    23  			return
    24  		}
    25  		ctc := makeChatTestContext(t, "TestChatSubteamRename", 2)
    26  		defer ctc.cleanup()
    27  		users := ctc.users()
    28  
    29  		listener1 := newServerChatListener()
    30  		listener2 := newServerChatListener()
    31  		ctc.as(t, users[0]).h.G().NotifyRouter.AddListener(listener1)
    32  		ctc.as(t, users[1]).h.G().NotifyRouter.AddListener(listener2)
    33  		ctc.world.Tcs[users[0].Username].ChatG.Syncer.(*Syncer).isConnected = true
    34  		ctc.world.Tcs[users[1].Username].ChatG.Syncer.(*Syncer).isConnected = true
    35  
    36  		// Root team conv
    37  		teamConv := mustCreateConversationForTest(t, ctc, users[0], chat1.TopicType_CHAT, chat1.ConversationMembersType_TEAM, ctc.as(t, users[1]).user())
    38  		// setup a sub team with user0, and user1, and a subsub team with only
    39  		// user1. both subteams have two channels.
    40  		parentTeamName, err := keybase1.TeamNameFromString(teamConv.TlfName)
    41  		require.NoError(t, err)
    42  		tc := ctc.world.Tcs[users[0].Username]
    43  
    44  		subteamBasename := "level1"
    45  		_, err = teams.CreateSubteam(context.TODO(), tc.G, subteamBasename, parentTeamName, keybase1.TeamRole_WRITER /* addSelfAs */)
    46  		require.NoError(t, err)
    47  		subteamName, err := parentTeamName.Append(subteamBasename)
    48  		require.NoError(t, err)
    49  		err = teams.SetRoleWriter(context.TODO(), tc.G, subteamName.String(), users[1].Username)
    50  		require.NoError(t, err)
    51  		ctc.teamCache[subteamName.String()] = subteamName.String()
    52  
    53  		subSubteamBasename := "level2"
    54  		_, err = teams.CreateSubteam(context.TODO(), tc.G, subSubteamBasename, subteamName, keybase1.TeamRole_WRITER /* addSelfAs */)
    55  		require.NoError(t, err)
    56  		subSubteamName, err := subteamName.Append(subSubteamBasename)
    57  		require.NoError(t, err)
    58  		ctc.teamCache[subSubteamName.String()] = subSubteamName.String()
    59  
    60  		versMap := make(map[chat1.ConvIDStr]chat1.ConversationVers)
    61  		var convs []chat1.ConversationInfoLocal
    62  		for _, name := range []string{subteamName.String(), subSubteamName.String()} {
    63  			for i := 0; i < 2; i++ {
    64  				t.Logf("creating conv %v, subteam: %vv", i, name)
    65  				var topicName *string
    66  				if i > 0 {
    67  					s := fmt.Sprintf("chan-%v", i)
    68  					topicName = &s
    69  				}
    70  				ctx := ctc.as(t, users[0])
    71  				ncres, err := ctx.chatLocalHandler().NewConversationLocal(ctx.startCtx,
    72  					chat1.NewConversationLocalArg{
    73  						TlfName:          name,
    74  						TopicType:        chat1.TopicType_CHAT,
    75  						TopicName:        topicName,
    76  						TlfVisibility:    keybase1.TLFVisibility_PRIVATE,
    77  						MembersType:      chat1.ConversationMembersType_TEAM,
    78  						IdentifyBehavior: keybase1.TLFIdentifyBehavior_CHAT_CLI,
    79  					})
    80  				require.NoError(t, err)
    81  				convs = append(convs, ncres.Conv.Info)
    82  				versMap[ncres.Conv.GetConvID().ConvIDStr()] = ncres.Conv.Info.Version
    83  
    84  				// Write a message so we have something that uses the old team name in the chat history.
    85  				_, err = ctc.as(t, users[0]).chatLocalHandler().PostLocal(context.TODO(), chat1.PostLocalArg{
    86  					ConversationID: ncres.Conv.Info.Id,
    87  					Msg: chat1.MessagePlaintext{
    88  						ClientHeader: chat1.MessageClientHeader{
    89  							Conv:        ncres.Conv.Info.Triple,
    90  							MessageType: chat1.MessageType_TEXT,
    91  							TlfName:     ncres.Conv.Info.TlfName,
    92  						},
    93  						MessageBody: chat1.NewMessageBodyWithText(chat1.MessageText{
    94  							Body: "Hello",
    95  						}),
    96  					},
    97  				})
    98  				require.NoError(t, err)
    99  			}
   100  		}
   101  
   102  		// subteam convs
   103  		subConv1 := convs[0] // u1, u2
   104  		subConv2 := convs[1] // u1
   105  		// subSubteam convs
   106  		subSubConv1 := convs[2] // u1
   107  		subSubConv2 := convs[3] // u1
   108  
   109  		// Rename the subteam
   110  		newSubteamName, err := parentTeamName.Append("bb2")
   111  		require.NoError(t, err)
   112  		err = teams.RenameSubteam(context.TODO(), tc.G, subteamName, newSubteamName)
   113  		require.NoError(t, err)
   114  		newSubSubteamName, err := newSubteamName.Append(subSubteamBasename)
   115  		require.NoError(t, err)
   116  
   117  		u1ExpectedUpdates := []chat1.ConversationID{
   118  			subConv1.Id,
   119  			subConv2.Id,
   120  			subSubConv1.Id,
   121  			subSubConv2.Id,
   122  		}
   123  		sort.Sort(utils.ByConvID(u1ExpectedUpdates))
   124  
   125  		u1Updates := consumeSubteamRename(t, listener1)
   126  		sort.Sort(utils.ByConvID(u1Updates))
   127  		require.Equal(t, u1ExpectedUpdates, u1Updates)
   128  
   129  		select {
   130  		case <-listener1.subteamRename:
   131  			require.Fail(t, "unexpected update")
   132  		case <-time.After(2 * time.Second):
   133  		}
   134  
   135  		u2ExpectedUpdates := []chat1.ConversationID{
   136  			subConv1.Id,
   137  		}
   138  
   139  		u2Updates := consumeSubteamRename(t, listener2)
   140  		require.Equal(t, u2ExpectedUpdates, u2Updates)
   141  
   142  		select {
   143  		case <-listener2.subteamRename:
   144  			require.Fail(t, "unexpected update")
   145  		case <-time.After(2 * time.Second):
   146  		}
   147  		ib, _, err := tc.ChatG.InboxSource.Read(context.TODO(), users[0].User.GetUID().ToBytes(),
   148  			types.ConversationLocalizerBlocking, types.InboxSourceDataSourceAll, nil,
   149  			&chat1.GetInboxLocalQuery{
   150  				ConvIDs: u1ExpectedUpdates,
   151  			})
   152  		require.NoError(t, err)
   153  		require.True(t, len(ib.Convs) >= len(u1ExpectedUpdates))
   154  
   155  		numFound := 0
   156  		for _, conv := range ib.Convs {
   157  			convID := conv.GetConvID()
   158  			if convID.Eq(subConv1.Id) || convID.Eq(subConv2.Id) {
   159  				require.Equal(t, newSubteamName.String(), conv.Info.TlfName)
   160  				numFound++
   161  			} else if convID.Eq(subSubConv1.Id) || convID.Eq(subSubConv2.Id) {
   162  				require.Equal(t, newSubSubteamName.String(), conv.Info.TlfName)
   163  				numFound++
   164  			}
   165  			require.NotEqual(t, versMap[conv.GetConvID().ConvIDStr()], conv.Info.Version)
   166  
   167  			// Make sure we can send to each conversation
   168  			_, err = ctc.as(t, users[0]).chatLocalHandler().PostLocal(context.TODO(), chat1.PostLocalArg{
   169  				ConversationID: convID,
   170  				Msg: chat1.MessagePlaintext{
   171  					ClientHeader: chat1.MessageClientHeader{
   172  						Conv:        conv.Info.Triple,
   173  						MessageType: chat1.MessageType_TEXT,
   174  						TlfName:     conv.Info.TlfName,
   175  					},
   176  					MessageBody: chat1.NewMessageBodyWithText(chat1.MessageText{
   177  						Body: "Hello",
   178  					}),
   179  				},
   180  			})
   181  			require.NoError(t, err)
   182  			// Make sure user1 (user0 did all the sends) can decrypt everything
   183  			// in conversation.
   184  			tv, err := tc.Context().ConvSource.Pull(context.TODO(), convID, users[1].GetUID().ToBytes(),
   185  				chat1.GetThreadReason_GENERAL, nil, nil, nil)
   186  			require.NoError(t, err)
   187  			for _, msg := range tv.Messages {
   188  				if msg.IsJourneycard() {
   189  					continue
   190  				}
   191  				require.True(t, msg.IsValid())
   192  			}
   193  		}
   194  		require.Equal(t, len(u1ExpectedUpdates), numFound)
   195  	})
   196  }