github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/retry_test.go (about) 1 package chat 2 3 import ( 4 "fmt" 5 "sort" 6 "testing" 7 "time" 8 9 "github.com/keybase/client/go/chat/globals" 10 "github.com/keybase/client/go/chat/storage" 11 "github.com/keybase/client/go/chat/types" 12 "github.com/keybase/client/go/kbtest" 13 "github.com/keybase/client/go/protocol/chat1" 14 "github.com/keybase/go-framed-msgpack-rpc/rpc" 15 "github.com/stretchr/testify/require" 16 "golang.org/x/net/context" 17 ) 18 19 type errorClient struct{} 20 21 func (e errorClient) Call(ctx context.Context, method string, arg interface{}, 22 res interface{}, timeout time.Duration) error { 23 return fmt.Errorf("errorClient: Call %s", method) 24 } 25 26 func (e errorClient) CallCompressed(ctx context.Context, method string, arg interface{}, 27 res interface{}, ctype rpc.CompressionType, timeout time.Duration) error { 28 return fmt.Errorf("errorClient: Call %s", method) 29 } 30 31 func (e errorClient) Notify(ctx context.Context, method string, arg interface{}, timeout time.Duration) error { 32 return fmt.Errorf("errorClient: Notify %s", method) 33 } 34 35 func TestFetchRetry(t *testing.T) { 36 ctx, world, ri2, _, sender, list := setupTest(t, 3) 37 defer world.Cleanup() 38 39 ri := ri2.(*kbtest.ChatRemoteMock) 40 rifunc := func() chat1.RemoteInterface { return ri } 41 u := world.GetUsers()[0] 42 u1 := world.GetUsers()[1] 43 u2 := world.GetUsers()[2] 44 uid := u.User.GetUID().ToBytes() 45 tc := world.Tcs[u.Username] 46 store := storage.New(globals.NewContext(tc.G, tc.ChatG), tc.ChatG.ConvSource) 47 48 var convIDs []chat1.ConversationID 49 var convs []chat1.Conversation 50 _, conv := newConv(ctx, t, tc, uid, ri, sender, u.Username+","+u1.Username) 51 convs = append(convs, conv) 52 _, conv = newConv(ctx, t, tc, uid, ri, sender, u.Username+","+u2.Username) 53 convs = append(convs, conv) 54 _, conv = newConv(ctx, t, tc, uid, ri, sender, u.Username+","+u2.Username+","+u1.Username) 55 convs = append(convs, conv) 56 sort.Slice(convs, func(i, j int) bool { 57 return convs[i].GetConvID().Less(convs[j].GetConvID()) 58 }) 59 for _, conv := range convs { 60 convIDs = append(convIDs, conv.GetConvID()) 61 } 62 63 // Nuke body cache 64 t.Logf("clearing: %s", convs[0].GetConvID()) 65 require.NoError(t, store.ClearAll(context.TODO(), convs[0].GetConvID(), uid)) 66 67 errorRI := func() chat1.RemoteInterface { return chat1.RemoteClient{Cli: errorClient{}} } 68 tc.ChatG.ConvSource.SetRemoteInterface(errorRI) 69 70 inbox, _, err := tc.ChatG.InboxSource.Read(ctx, uid, types.ConversationLocalizerBlocking, 71 types.InboxSourceDataSourceAll, nil, 72 &chat1.GetInboxLocalQuery{ 73 ConvIDs: convIDs, 74 }) 75 require.NoError(t, err) 76 sort.Slice(inbox.Convs, func(i, j int) bool { 77 return inbox.Convs[i].GetConvID().Less(inbox.Convs[j].GetConvID()) 78 }) 79 require.NotNil(t, inbox.Convs[0].Error) 80 require.Nil(t, inbox.Convs[1].Error) 81 require.Nil(t, inbox.Convs[2].Error) 82 tc.ChatG.FetchRetrier.Failure(ctx, uid, 83 NewConversationRetry(tc.Context(), inbox.Convs[0].GetConvID(), nil, ThreadLoad)) 84 85 // Advance clock and check for errors on all conversations 86 t.Logf("advancing clock and checking for stale") 87 tc.ChatG.ConvSource.SetRemoteInterface(rifunc) 88 world.Fc.Advance(time.Hour) 89 select { 90 case updates := <-list.threadsStale: 91 require.Equal(t, 1, len(updates)) 92 require.Equal(t, chat1.StaleUpdateType_NEWACTIVITY, updates[0].UpdateType) 93 case <-time.After(20 * time.Second): 94 require.Fail(t, "timeout on inbox stale") 95 } 96 world.Fc.Advance(time.Hour) 97 select { 98 case <-list.threadsStale: 99 require.Fail(t, "invalid stale message") 100 default: 101 } 102 103 t.Logf("trying to use Force") 104 tc.ChatG.FetchRetrier.Failure(ctx, uid, 105 NewConversationRetry(tc.Context(), inbox.Convs[0].GetConvID(), nil, ThreadLoad)) 106 tc.ChatG.FetchRetrier.Force(ctx) 107 select { 108 case cids := <-list.threadsStale: 109 require.Equal(t, 1, len(cids)) 110 case <-time.After(20 * time.Second): 111 require.Fail(t, "timeout on inbox stale") 112 } 113 114 t.Logf("testing full inbox retry") 115 ttype := chat1.TopicType_CHAT 116 tc.Context().FetchRetrier.Failure(ctx, uid, 117 NewFullInboxRetry(tc.Context(), &chat1.GetInboxLocalQuery{ 118 TopicType: &ttype, 119 })) 120 tc.Context().FetchRetrier.Force(ctx) 121 select { 122 case <-list.inboxStale: 123 case <-time.After(20 * time.Second): 124 require.Fail(t, "no inbox full stale received") 125 } 126 127 }