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  }