github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/chat/inboxsource_test.go (about) 1 package chat 2 3 import ( 4 "fmt" 5 "testing" 6 7 "sync" 8 9 context "golang.org/x/net/context" 10 11 "github.com/keybase/client/go/chat/storage" 12 "github.com/keybase/client/go/chat/types" 13 "github.com/keybase/client/go/chat/utils" 14 "github.com/keybase/client/go/kbtest" 15 "github.com/keybase/client/go/protocol/chat1" 16 "github.com/keybase/client/go/protocol/gregor1" 17 "github.com/stretchr/testify/require" 18 ) 19 20 func TestInboxSourceUpdateRace(t *testing.T) { 21 ctx, world, ri, _, sender, _ := setupTest(t, 1) 22 defer world.Cleanup() 23 24 u := world.GetUsers()[0] 25 uid := u.User.GetUID().ToBytes() 26 tc := world.Tcs[u.Username] 27 conv := newBlankConv(ctx, t, tc, uid, ri, sender, u.Username) 28 29 _, _, err := sender.Send(ctx, conv.GetConvID(), chat1.MessagePlaintext{ 30 ClientHeader: chat1.MessageClientHeader{ 31 Conv: conv.Metadata.IdTriple, 32 Sender: u.User.GetUID().ToBytes(), 33 TlfName: u.Username, 34 TlfPublic: false, 35 MessageType: chat1.MessageType_TEXT, 36 }, 37 MessageBody: chat1.NewMessageBodyWithText(chat1.MessageText{ 38 Body: "HIHI", 39 }), 40 }, 0, nil, nil, nil) 41 require.NoError(t, err) 42 43 ib, _, err := tc.ChatG.InboxSource.Read(ctx, u.User.GetUID().ToBytes(), 44 types.ConversationLocalizerBlocking, types.InboxSourceDataSourceAll, nil, nil) 45 require.NoError(t, err) 46 require.Equal(t, chat1.InboxVers(0), ib.Version, "wrong version") 47 48 // Spawn two goroutines to try and update the inbox at the same time with a self-update, and a 49 // Gregor style update 50 t.Logf("spawning update goroutines") 51 var wg sync.WaitGroup 52 53 wg.Add(1) 54 go func() { 55 _, err = tc.ChatG.InboxSource.SetStatus(ctx, uid, 0, conv.GetConvID(), 56 chat1.ConversationStatus_UNFILED) 57 require.NoError(t, err) 58 wg.Done() 59 }() 60 wg.Add(1) 61 go func() { 62 _, err = tc.ChatG.InboxSource.SetStatus(ctx, uid, 1, conv.GetConvID(), 63 chat1.ConversationStatus_UNFILED) 64 require.NoError(t, err) 65 wg.Done() 66 }() 67 wg.Wait() 68 69 ib, _, err = tc.ChatG.InboxSource.Read(ctx, u.User.GetUID().ToBytes(), 70 types.ConversationLocalizerBlocking, types.InboxSourceDataSourceAll, nil, nil) 71 require.NoError(t, err) 72 require.Equal(t, chat1.InboxVers(1), ib.Version, "wrong version") 73 } 74 75 // Test that when an update is received that is more than 1 ahead of the current inbox version, 76 // a complete sync of the inbox occurs. 77 func TestInboxSourceSkipAhead(t *testing.T) { 78 t.Logf("setup") 79 ctx, world, ri2, _, sender, _ := setupTest(t, 1) 80 ri := ri2.(*kbtest.ChatRemoteMock) 81 defer world.Cleanup() 82 t.Logf("test's remoteInterface: %p[%T] -> %v", &ri, ri, ri) 83 84 u := world.GetUsers()[0] 85 tc := world.Tcs[u.Username] 86 uid := u.User.GetUID().ToBytes() 87 88 assertInboxVersion := func(v int) { 89 ib, _, err := tc.ChatG.InboxSource.Read(ctx, u.User.GetUID().ToBytes(), 90 types.ConversationLocalizerBlocking, types.InboxSourceDataSourceAll, nil, nil) 91 require.Equal(t, chat1.InboxVers(v), ib.Version, "wrong version") 92 require.NoError(t, err) 93 } 94 95 fatal := func(msg string, args ...interface{}) error { 96 t.Fatalf(msg, args...) 97 return fmt.Errorf(msg, args...) 98 } 99 100 t.Logf("install fake sync") 101 ri.SyncInboxFunc = func(m *kbtest.ChatRemoteMock, ctx context.Context, vers chat1.InboxVers) (chat1.SyncInboxRes, error) { 102 return chat1.SyncInboxRes{}, fatal("sync not expected yet") 103 } 104 105 assertInboxVersion(0) 106 107 t.Logf("new conv") 108 conv := newBlankConv(ctx, t, tc, uid, ri, sender, u.Username) 109 110 assertInboxVersion(0) 111 112 t.Logf("add message but drop oobm") 113 114 rc := utils.RemoteConv(conv) 115 localConvs, _, err := tc.Context().InboxSource.Localize(ctx, uid, []types.RemoteConversation{rc}, 116 types.ConversationLocalizerBlocking) 117 require.NoError(t, err) 118 require.Equal(t, 1, len(localConvs)) 119 prepareRes, err := sender.Prepare(ctx, chat1.MessagePlaintext{ 120 ClientHeader: chat1.MessageClientHeader{ 121 Conv: conv.Metadata.IdTriple, 122 Sender: u.User.GetUID().ToBytes(), 123 TlfName: u.Username, 124 TlfPublic: false, 125 MessageType: chat1.MessageType_TEXT, 126 }, 127 MessageBody: chat1.NewMessageBodyWithText(chat1.MessageText{ 128 Body: "HIHI", 129 }), 130 }, chat1.ConversationMembersType_KBFS, &localConvs[0], nil) 131 require.NoError(t, err) 132 boxed := prepareRes.Boxed 133 134 postRes, err := ri.PostRemote(ctx, chat1.PostRemoteArg{ 135 ConversationID: conv.GetConvID(), 136 MessageBoxed: boxed, 137 }) 138 require.NoError(t, err) 139 boxed.ServerHeader = &postRes.MsgHeader 140 141 assertInboxVersion(0) 142 143 t.Logf("install fake sync") 144 syncCalled := 0 145 ri.SyncInboxFunc = func(m *kbtest.ChatRemoteMock, ctx context.Context, vers chat1.InboxVers) (chat1.SyncInboxRes, error) { 146 syncCalled++ 147 require.Equal(t, chat1.InboxVers(0), vers) 148 149 res, err := m.GetInboxRemote(ctx, chat1.GetInboxRemoteArg{ 150 Vers: vers, 151 Query: nil, 152 Pagination: nil, 153 }) 154 require.NoError(t, err) 155 156 return chat1.NewSyncInboxResWithIncremental(chat1.SyncIncrementalRes{ 157 Vers: 100, 158 Convs: res.Inbox.Full().Conversations, 159 }), nil 160 } 161 162 t.Logf("receive oobm with version light years ahead of its current one") 163 _, err = tc.ChatG.InboxSource.NewMessage(context.TODO(), u.User.GetUID().ToBytes(), chat1.InboxVers(100), 164 conv.GetConvID(), boxed, nil) 165 require.NoError(t, err) 166 assertInboxVersion(100) 167 168 t.Logf("sync was triggered") 169 require.Equal(t, 1, syncCalled) 170 } 171 172 func TestInboxSourceLocalOnly(t *testing.T) { 173 ctc := makeChatTestContext(t, "TestInboxSourceLocalOnly", 1) 174 defer ctc.cleanup() 175 users := ctc.users() 176 useRemoteMock = false 177 defer func() { useRemoteMock = true }() 178 179 listener := newServerChatListener() 180 ctc.as(t, users[0]).h.G().NotifyRouter.AddListener(listener) 181 ctc.world.Tcs[users[0].Username].ChatG.UIInboxLoader = types.DummyUIInboxLoader{} 182 ctc.world.Tcs[users[0].Username].ChatG.Syncer.(*Syncer).isConnected = true 183 184 ctx := ctc.as(t, users[0]).startCtx 185 tc := ctc.world.Tcs[users[0].Username] 186 uid := users[0].User.GetUID().ToBytes() 187 188 conv := mustCreateConversationForTest(t, ctc, users[0], chat1.TopicType_CHAT, 189 chat1.ConversationMembersType_IMPTEAMNATIVE) 190 consumeNewConversation(t, listener, conv.Id) 191 192 attempt := func(mode types.InboxSourceDataSourceTyp, success bool) { 193 ib, err := tc.Context().InboxSource.ReadUnverified(ctx, uid, mode, 194 &chat1.GetInboxQuery{ 195 ConvID: &conv.Id, 196 }) 197 if success { 198 require.NoError(t, err) 199 require.Equal(t, 1, len(ib.ConvsUnverified)) 200 require.Equal(t, conv.Id, ib.ConvsUnverified[0].GetConvID()) 201 } else { 202 require.Error(t, err) 203 require.IsType(t, storage.MissError{}, err) 204 } 205 } 206 207 attempt(types.InboxSourceDataSourceAll, true) 208 attempt(types.InboxSourceDataSourceLocalOnly, true) 209 require.NoError(t, tc.Context().InboxSource.Clear(ctx, uid, nil)) 210 attempt(types.InboxSourceDataSourceLocalOnly, false) 211 attempt(types.InboxSourceDataSourceRemoteOnly, true) 212 attempt(types.InboxSourceDataSourceLocalOnly, false) 213 attempt(types.InboxSourceDataSourceAll, true) 214 attempt(types.InboxSourceDataSourceLocalOnly, true) 215 } 216 217 func TestChatConversationDeleted(t *testing.T) { 218 runWithMemberTypes(t, func(mt chat1.ConversationMembersType) { 219 switch mt { 220 case chat1.ConversationMembersType_TEAM: 221 default: 222 return 223 } 224 ctc := makeChatTestContext(t, "TestChatConversationDeleted", 1) 225 defer ctc.cleanup() 226 users := ctc.users() 227 ctx := context.TODO() 228 uid := gregor1.UID(users[0].User.GetUID().ToBytes()) 229 ctc.as(t, users[0]) 230 g := ctc.world.Tcs[users[0].Username].Context() 231 _, _, err := g.InboxSource.Read(ctx, uid, types.ConversationLocalizerBlocking, types.InboxSourceDataSourceRemoteOnly, nil, 232 &chat1.GetInboxLocalQuery{ 233 ConvIDs: []chat1.ConversationID{chat1.ConversationID("dead")}, 234 }) 235 require.NoError(t, err) 236 }) 237 }