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  }