github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/storage/inbox_test.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"sort"
     6  	"testing"
     7  	"time"
     8  
     9  	"encoding/hex"
    10  
    11  	"github.com/keybase/client/go/chat/types"
    12  	"github.com/keybase/client/go/chat/utils"
    13  	"github.com/keybase/client/go/kbtest"
    14  	"github.com/keybase/client/go/libkb"
    15  	"github.com/keybase/client/go/protocol/chat1"
    16  	"github.com/keybase/client/go/protocol/gregor1"
    17  	"github.com/keybase/client/go/protocol/keybase1"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func setupInboxTest(t testing.TB, name string) (kbtest.ChatTestContext, *Inbox, gregor1.UID) {
    22  	ctc := setupCommonTest(t, name)
    23  
    24  	u, err := kbtest.CreateAndSignupFakeUser("ib", ctc.TestContext.G)
    25  	require.NoError(t, err)
    26  	uid := gregor1.UID(u.User.GetUID().ToBytes())
    27  	return ctc, NewInbox(ctc.Context()), uid
    28  }
    29  
    30  func makeTlfID() chat1.TLFID {
    31  	return randBytes(8)
    32  }
    33  
    34  func makeConvo(mtime gregor1.Time, rmsg chat1.MessageID, mmsg chat1.MessageID) types.RemoteConversation {
    35  	conv := chat1.Conversation{
    36  		Metadata: chat1.ConversationMetadata{
    37  			ConversationID: randBytes(8),
    38  			IdTriple: chat1.ConversationIDTriple{
    39  				Tlfid:     makeTlfID(),
    40  				TopicType: chat1.TopicType_CHAT,
    41  				TopicID:   randBytes(8),
    42  			},
    43  			Visibility: keybase1.TLFVisibility_PRIVATE,
    44  			Status:     chat1.ConversationStatus_UNFILED,
    45  		},
    46  		ReaderInfo: &chat1.ConversationReaderInfo{
    47  			Mtime:     mtime,
    48  			ReadMsgid: rmsg,
    49  			MaxMsgid:  mmsg,
    50  		},
    51  		// Make it look like there's a visible message in here too
    52  		MaxMsgSummaries: []chat1.MessageSummary{{MessageType: chat1.MessageType_TEXT, MsgID: 1}},
    53  	}
    54  	return utils.RemoteConv(conv)
    55  }
    56  
    57  func makeInboxMsg(id chat1.MessageID, typ chat1.MessageType) chat1.MessageBoxed {
    58  	return chat1.MessageBoxed{
    59  		ClientHeader: chat1.MessageClientHeader{
    60  			MessageType: typ,
    61  		},
    62  		ServerHeader: &chat1.MessageServerHeader{
    63  			MessageID: id,
    64  			Ctime:     gregor1.ToTime(time.Now()),
    65  		},
    66  	}
    67  }
    68  
    69  func convListCompare(t *testing.T, ref []types.RemoteConversation, res []types.RemoteConversation,
    70  	name string) {
    71  	require.Equal(t, len(ref), len(res), name+" size mismatch")
    72  	refMap := make(map[chat1.ConvIDStr]types.RemoteConversation)
    73  	for _, conv := range ref {
    74  		refMap[conv.GetConvID().ConvIDStr()] = conv
    75  	}
    76  	for _, conv := range res {
    77  		require.Equal(t, refMap[conv.GetConvID().ConvIDStr()], conv)
    78  	}
    79  }
    80  
    81  func TestInboxBasic(t *testing.T) {
    82  
    83  	tc, inbox, uid := setupInboxTest(t, "basic")
    84  	defer tc.Cleanup()
    85  
    86  	// Create an inbox with a bunch of convos, merge it and read it back out
    87  	numConvs := 10
    88  	var convs []types.RemoteConversation
    89  	for i := numConvs - 1; i >= 0; i-- {
    90  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
    91  	}
    92  
    93  	// Fetch with no query parameter
    94  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
    95  	vers, res, err := inbox.Read(context.TODO(), uid, nil)
    96  
    97  	require.NoError(t, err)
    98  	require.Equal(t, chat1.InboxVers(1), vers, "version mismatch")
    99  	convListCompare(t, convs, res, "basic")
   100  }
   101  
   102  func TestInboxSummarize(t *testing.T) {
   103  	tc, inbox, uid := setupInboxTest(t, "summarize")
   104  	defer tc.Cleanup()
   105  
   106  	conv := makeConvo(gregor1.Time(1), 1, 1)
   107  	maxMsgID := chat1.MessageID(6)
   108  	conv.Conv.MaxMsgs = []chat1.MessageBoxed{{
   109  		ClientHeader: chat1.MessageClientHeader{
   110  			MessageType: chat1.MessageType_TEXT,
   111  		},
   112  		ServerHeader: &chat1.MessageServerHeader{
   113  			MessageID: maxMsgID,
   114  		},
   115  	}}
   116  
   117  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, []chat1.Conversation{conv.Conv}, nil))
   118  	_, res, err := inbox.Read(context.TODO(), uid, nil)
   119  	require.NoError(t, err)
   120  	require.Zero(t, len(res[0].Conv.MaxMsgs))
   121  	require.Equal(t, 1, len(res[0].Conv.MaxMsgSummaries))
   122  	require.Equal(t, maxMsgID, res[0].Conv.MaxMsgSummaries[0].GetMessageID())
   123  }
   124  
   125  func TestInboxQueries(t *testing.T) {
   126  	tc, inbox, uid := setupInboxTest(t, "queries")
   127  	defer tc.Cleanup()
   128  
   129  	// Create an inbox with a bunch of convos, merge it and read it back out
   130  	numConvs := 20
   131  	var convs []types.RemoteConversation
   132  	for i := 0; i < numConvs; i++ {
   133  		conv := makeConvo(gregor1.Time(i), 1, 1)
   134  		convs = append(convs, conv)
   135  	}
   136  
   137  	// Make two dev convos
   138  	var devs, publics, unreads, ignored, muted, full []types.RemoteConversation
   139  	convs[3].Conv.Metadata.IdTriple.TopicType = chat1.TopicType_DEV
   140  	convs[7].Conv.Metadata.IdTriple.TopicType = chat1.TopicType_DEV
   141  	devs = append(devs, []types.RemoteConversation{convs[7], convs[3]}...)
   142  
   143  	// Make one public convos
   144  	convs[13].Conv.Metadata.Visibility = keybase1.TLFVisibility_PUBLIC
   145  	publics = append(publics, convs[13])
   146  
   147  	// Make three unread convos
   148  	makeUnread := func(ri *chat1.ConversationReaderInfo) {
   149  		ri.ReadMsgid = 0
   150  	}
   151  	makeUnread(convs[5].Conv.ReaderInfo)
   152  	makeUnread(convs[13].Conv.ReaderInfo)
   153  	makeUnread(convs[19].Conv.ReaderInfo)
   154  	unreads = append(unreads, []types.RemoteConversation{convs[19], convs[13], convs[5]}...)
   155  
   156  	// Make two ignored
   157  	convs[18].Conv.Metadata.Status = chat1.ConversationStatus_IGNORED
   158  	convs[4].Conv.Metadata.Status = chat1.ConversationStatus_IGNORED
   159  	ignored = append(ignored, []types.RemoteConversation{convs[18], convs[4]}...)
   160  
   161  	// Make one muted
   162  	convs[12].Conv.Metadata.Status = chat1.ConversationStatus_MUTED
   163  	muted = append(muted, []types.RemoteConversation{convs[12]}...)
   164  
   165  	// Mark one as finalized and superseded by
   166  	convs[6].Conv.Metadata.FinalizeInfo = &chat1.ConversationFinalizeInfo{
   167  		ResetFull: "reset",
   168  	}
   169  	convs[6].Conv.Metadata.SupersededBy = append(convs[6].Conv.Metadata.SupersededBy, convs[17].Conv.Metadata)
   170  	convs[17].Conv.Metadata.Supersedes = append(convs[17].Conv.Metadata.Supersedes, convs[6].Conv.Metadata)
   171  	for i := len(convs) - 1; i >= 0; i-- {
   172  		if i == 6 {
   173  			continue
   174  		}
   175  		full = append(full, convs[i])
   176  	}
   177  	for _, conv := range full {
   178  		t.Logf("convID: %s", conv.GetConvID())
   179  	}
   180  
   181  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   182  
   183  	// Merge in queries and try to read them back out
   184  	var q *chat1.GetInboxQuery
   185  	mergeReadAndCheck := func(t *testing.T, ref []types.RemoteConversation, name string) {
   186  		require.NoError(t, inbox.Merge(context.TODO(), uid, 1, []chat1.Conversation{}, q))
   187  		_, res, err := inbox.Read(context.TODO(), uid, q)
   188  		require.NoError(t, err)
   189  		convListCompare(t, ref, res, name)
   190  	}
   191  	t.Logf("merging all convs with nil query")
   192  	q = nil
   193  	mergeReadAndCheck(t, full, "all")
   194  
   195  	t.Logf("merging dev query")
   196  	devtype := chat1.TopicType_DEV
   197  	q = &chat1.GetInboxQuery{TopicType: &devtype}
   198  	mergeReadAndCheck(t, devs, "devs")
   199  
   200  	t.Logf("merging public query")
   201  	publicVis := keybase1.TLFVisibility_PUBLIC
   202  	q = &chat1.GetInboxQuery{TlfVisibility: &publicVis}
   203  	mergeReadAndCheck(t, publics, "public")
   204  
   205  	t.Logf("merging unread query")
   206  	q = &chat1.GetInboxQuery{UnreadOnly: true}
   207  	mergeReadAndCheck(t, unreads, "unread")
   208  
   209  	t.Logf("merging ignore query")
   210  	q = &chat1.GetInboxQuery{Status: []chat1.ConversationStatus{chat1.ConversationStatus_IGNORED}}
   211  	mergeReadAndCheck(t, ignored, "ignored")
   212  
   213  	t.Logf("merging muted query")
   214  	q = &chat1.GetInboxQuery{Status: []chat1.ConversationStatus{chat1.ConversationStatus_MUTED}}
   215  	mergeReadAndCheck(t, muted, "muted")
   216  
   217  	t.Logf("merging tlf ID query")
   218  	q = &chat1.GetInboxQuery{TlfID: &full[0].Conv.Metadata.IdTriple.Tlfid}
   219  	tlfIDs := []types.RemoteConversation{full[0]}
   220  	mergeReadAndCheck(t, tlfIDs, "tlfids")
   221  
   222  	t.Logf("merging after query")
   223  	after := full[:4]
   224  	atime := gregor1.Time(15)
   225  	q = &chat1.GetInboxQuery{After: &atime}
   226  	mergeReadAndCheck(t, after, "after")
   227  
   228  	t.Logf("merging before query")
   229  	before := full[5:]
   230  	var beforeConvIDs []chat1.ConversationID
   231  	for _, bconv := range before {
   232  		beforeConvIDs = append(beforeConvIDs, bconv.GetConvID())
   233  	}
   234  	btime := gregor1.Time(15)
   235  	q = &chat1.GetInboxQuery{Before: &btime}
   236  	mergeReadAndCheck(t, before, "before")
   237  
   238  	t.Logf("check conv IDs queries work")
   239  	q = &chat1.GetInboxQuery{Before: &btime, ConvIDs: beforeConvIDs}
   240  	_, cres, err := inbox.Read(context.TODO(), uid, q)
   241  	require.NoError(t, err)
   242  	convListCompare(t, before, cres, "convIDs")
   243  }
   244  
   245  func TestInboxEmptySuperseder(t *testing.T) {
   246  	tc, inbox, uid := setupInboxTest(t, "queries")
   247  	defer tc.Cleanup()
   248  
   249  	// Create an inbox with a bunch of convos, merge it and read it back out
   250  	numConvs := 20
   251  	var convs []types.RemoteConversation
   252  	for i := 0; i < numConvs; i++ {
   253  		conv := makeConvo(gregor1.Time(i), 1, 1)
   254  		conv.Conv.MaxMsgSummaries = nil
   255  		convs = append(convs, conv)
   256  	}
   257  	var full, superseded []types.RemoteConversation
   258  	convs[6].Conv.Metadata.SupersededBy = append(convs[6].Conv.Metadata.SupersededBy, convs[17].Conv.Metadata)
   259  	convs[17].Conv.Metadata.Supersedes = append(convs[17].Conv.Metadata.Supersedes, convs[6].Conv.Metadata)
   260  	for i := len(convs) - 1; i >= 0; i-- {
   261  		// Don't skip the superseded one, since it's not supposed to be filtered out
   262  		// by an empty superseder
   263  		full = append(full, convs[i])
   264  	}
   265  	for _, conv := range full {
   266  		t.Logf("convID: %s", conv.GetConvID())
   267  	}
   268  
   269  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   270  
   271  	// Merge in queries and try to read them back out
   272  	var q *chat1.GetInboxQuery
   273  	mergeReadAndCheck := func(t *testing.T, ref []types.RemoteConversation, name string) {
   274  		require.NoError(t, inbox.Merge(context.TODO(), uid, 1, []chat1.Conversation{}, q))
   275  		_, res, err := inbox.Read(context.TODO(), uid, q)
   276  		require.NoError(t, err)
   277  		convListCompare(t, ref, res, name)
   278  	}
   279  	t.Logf("merging all convs with nil query")
   280  	q = nil
   281  	mergeReadAndCheck(t, full, "all")
   282  
   283  	t.Logf("merging empty superseder query")
   284  	// Don't skip the superseded one, since it's not supposed to be filtered out
   285  	// by an empty superseder
   286  	superseded = append(superseded, full...)
   287  	q = &chat1.GetInboxQuery{}
   288  	// OneChatTypePerTLF
   289  	t.Logf("full has %d, superseded has %d", len(full), len(superseded))
   290  
   291  	mergeReadAndCheck(t, superseded, "superseded")
   292  
   293  	// Now test OneChatTypePerTLF
   294  	tc, inbox, uid = setupInboxTest(t, "queries2")
   295  	defer tc.Cleanup()
   296  
   297  	full = []types.RemoteConversation{}
   298  	superseded = []types.RemoteConversation{}
   299  	for i := len(convs) - 1; i >= 0; i-- {
   300  		// skip the superseded one, since it's supposed to be filtered out
   301  		// if not OneChatTypePerTLF
   302  		if i == 6 {
   303  			continue
   304  		}
   305  		full = append(full, convs[i])
   306  	}
   307  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(full), nil))
   308  	superseded = append(superseded, full...)
   309  	oneChatTypePerTLF := false
   310  	q = &chat1.GetInboxQuery{OneChatTypePerTLF: &oneChatTypePerTLF}
   311  	mergeReadAndCheck(t, superseded, "superseded")
   312  
   313  }
   314  
   315  func TestInboxNewConversation(t *testing.T) {
   316  	tc, inbox, uid := setupInboxTest(t, "basic")
   317  	defer tc.Cleanup()
   318  
   319  	// Create an inbox with a bunch of convos, merge it and read it back out
   320  	numConvs := 10
   321  	var convs []types.RemoteConversation
   322  	for i := numConvs - 1; i >= 0; i-- {
   323  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   324  	}
   325  	convs[5].Conv.Metadata.FinalizeInfo = &chat1.ConversationFinalizeInfo{
   326  		ResetFull: "reset",
   327  	}
   328  
   329  	t.Logf("basic newconv")
   330  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   331  	newConv := makeConvo(gregor1.Time(11), 1, 1)
   332  	require.NoError(t, inbox.NewConversation(context.TODO(), uid, 2, newConv.Conv))
   333  	_, res, err := inbox.Read(context.TODO(), uid, nil)
   334  	require.NoError(t, err)
   335  	convs = append([]types.RemoteConversation{newConv}, convs...)
   336  	convListCompare(t, convs, res, "newconv")
   337  
   338  	t.Logf("repeat conv")
   339  	require.NoError(t, inbox.NewConversation(context.TODO(), uid, 3, newConv.Conv))
   340  	_, res, err = inbox.Read(context.TODO(), uid, nil)
   341  	require.NoError(t, err)
   342  	convListCompare(t, convs, res, "repeatconv")
   343  
   344  	t.Logf("supersede newconv")
   345  	newConv = makeConvo(gregor1.Time(12), 1, 1)
   346  	newConv.Conv.Metadata.Supersedes = append(newConv.Conv.Metadata.Supersedes, convs[6].Conv.Metadata)
   347  	require.NoError(t, inbox.NewConversation(context.TODO(), uid, 4, newConv.Conv))
   348  	_, res, err = inbox.Read(context.TODO(), uid, nil)
   349  	require.NoError(t, err)
   350  	convs = append([]types.RemoteConversation{newConv}, convs...)
   351  	convListCompare(t, append(convs[:7], convs[8:]...), res, "newconv finalized")
   352  
   353  	require.Equal(t, numConvs+2, len(convs), "n convs")
   354  
   355  	err = inbox.NewConversation(context.TODO(), uid, 10, newConv.Conv)
   356  	require.IsType(t, VersionMismatchError{}, err)
   357  }
   358  
   359  func TestInboxNewMessage(t *testing.T) {
   360  
   361  	tc, inbox, uid := setupInboxTest(t, "basic")
   362  	defer tc.Cleanup()
   363  
   364  	// Create an inbox with a bunch of convos, merge it and read it back out
   365  	numConvs := 10
   366  	var convs []types.RemoteConversation
   367  	for i := numConvs - 1; i >= 0; i-- {
   368  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   369  	}
   370  
   371  	uid1 := uid
   372  	uid2, err := hex.DecodeString("22")
   373  	require.NoError(t, err)
   374  	uid3, err := hex.DecodeString("33")
   375  	require.NoError(t, err)
   376  
   377  	convs[5].Conv.Metadata.ActiveList = []gregor1.UID{uid2, uid3, uid1}
   378  	convs[6].Conv.Metadata.ActiveList = []gregor1.UID{uid2, uid3}
   379  	conv := convs[5]
   380  	msg := makeInboxMsg(2, chat1.MessageType_TEXT)
   381  	msg.ClientHeader.Sender = uid1
   382  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   383  	convID := conv.GetConvID()
   384  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 2, conv.GetConvID(), msg, nil))
   385  	_, res, err := inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   386  		ConvID: &convID,
   387  	})
   388  	require.NoError(t, err)
   389  	require.Equal(t, conv.GetConvID(), res[0].GetConvID(), "conv not promoted")
   390  	require.Equal(t, chat1.MessageID(2), res[0].Conv.ReaderInfo.MaxMsgid, "wrong max msgid")
   391  	require.Equal(t, chat1.MessageID(2), res[0].Conv.ReaderInfo.ReadMsgid, "wrong read msgid")
   392  	require.Equal(t, msg.Ctime(), res[0].Conv.ReaderInfo.LastSendTime)
   393  	require.Equal(t, []gregor1.UID{uid1, uid2, uid3}, res[0].Conv.Metadata.ActiveList, "active list")
   394  	maxMsg, err := res[0].Conv.GetMaxMessage(chat1.MessageType_TEXT)
   395  	require.NoError(t, err)
   396  	require.Equal(t, chat1.MessageID(2), maxMsg.GetMessageID(), "max msg not updated")
   397  
   398  	// Test incomplete active list
   399  	conv = convs[6]
   400  	convID = conv.GetConvID()
   401  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 3, conv.GetConvID(), msg, nil))
   402  	_, res, err = inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   403  		ConvID: &convID,
   404  	})
   405  	require.NoError(t, err)
   406  	require.Equal(t, []gregor1.UID{uid1, uid2, uid3}, res[0].Conv.Metadata.ActiveList, "active list")
   407  
   408  	// Send another one from a diff User
   409  	msg2 := makeInboxMsg(3, chat1.MessageType_TEXT)
   410  	msg2.ClientHeader.Sender = uid2
   411  	convID = conv.GetConvID()
   412  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 4, conv.GetConvID(), msg2, nil))
   413  	_, res, err = inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   414  		ConvID: &convID,
   415  	})
   416  	require.NoError(t, err)
   417  	require.Equal(t, chat1.MessageID(3), res[0].Conv.ReaderInfo.MaxMsgid, "wrong max msgid")
   418  	require.Equal(t, chat1.MessageID(2), res[0].Conv.ReaderInfo.ReadMsgid, "wrong read msgid")
   419  	require.Equal(t, msg.Ctime(), res[0].Conv.ReaderInfo.LastSendTime)
   420  	require.Equal(t, []gregor1.UID{uid2, uid1, uid3}, res[0].Conv.Metadata.ActiveList, "active list")
   421  	maxMsg, err = res[0].Conv.GetMaxMessage(chat1.MessageType_TEXT)
   422  	require.NoError(t, err)
   423  	require.Equal(t, chat1.MessageID(3), maxMsg.GetMessageID())
   424  
   425  	// Test delete mechanics
   426  	delMsg := makeInboxMsg(4, chat1.MessageType_TEXT)
   427  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 5, conv.GetConvID(), delMsg, nil))
   428  	_, res, err = inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   429  		ConvID: &convID,
   430  	})
   431  	require.NoError(t, err)
   432  	maxMsg, err = res[0].Conv.GetMaxMessage(chat1.MessageType_TEXT)
   433  	require.NoError(t, err)
   434  	require.Equal(t, delMsg.GetMessageID(), maxMsg.GetMessageID())
   435  	delete := makeInboxMsg(5, chat1.MessageType_DELETE)
   436  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 0, conv.GetConvID(), delete, nil))
   437  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 6, conv.GetConvID(), delete,
   438  		[]chat1.MessageSummary{msg2.Summary()}))
   439  	_, res, err = inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   440  		ConvID: &convID,
   441  	})
   442  	require.NoError(t, err)
   443  	maxMsg, err = res[0].Conv.GetMaxMessage(chat1.MessageType_TEXT)
   444  	require.NoError(t, err)
   445  	require.Equal(t, msg2.GetMessageID(), maxMsg.GetMessageID())
   446  	delete = makeInboxMsg(6, chat1.MessageType_DELETE)
   447  	err = inbox.NewMessage(context.TODO(), uid, 7, conv.GetConvID(), delete, nil)
   448  	require.Error(t, err)
   449  	require.IsType(t, VersionMismatchError{}, err)
   450  
   451  	err = inbox.NewMessage(context.TODO(), uid, 10, conv.GetConvID(), msg2, nil)
   452  	require.IsType(t, VersionMismatchError{}, err)
   453  }
   454  
   455  func TestInboxReadMessage(t *testing.T) {
   456  
   457  	tc, inbox, uid := setupInboxTest(t, "basic")
   458  	defer tc.Cleanup()
   459  
   460  	uid2, err := hex.DecodeString("22")
   461  	require.NoError(t, err)
   462  
   463  	// Create an inbox with a bunch of convos, merge it and read it back out
   464  	numConvs := 10
   465  	var convs []types.RemoteConversation
   466  	for i := numConvs - 1; i >= 0; i-- {
   467  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   468  	}
   469  
   470  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   471  	_, _, err = inbox.Read(context.TODO(), uid, nil)
   472  	require.NoError(t, err)
   473  
   474  	conv := convs[5]
   475  	convID := conv.GetConvID()
   476  	msg := makeInboxMsg(2, chat1.MessageType_TEXT)
   477  	msg.ClientHeader.Sender = uid2
   478  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 2, conv.GetConvID(), msg, nil))
   479  	_, res, err := inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   480  		ConvID: &convID,
   481  	})
   482  	require.NoError(t, err)
   483  	require.Equal(t, chat1.MessageID(2), res[0].Conv.ReaderInfo.MaxMsgid, "wrong max msgid")
   484  	require.Equal(t, chat1.MessageID(1), res[0].Conv.ReaderInfo.ReadMsgid, "wrong read msgid")
   485  	require.Equal(t, gregor1.Time(0), res[0].Conv.ReaderInfo.LastSendTime)
   486  	require.NoError(t, inbox.ReadMessage(context.TODO(), uid, 3, conv.GetConvID(), 2))
   487  	_, res, err = inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   488  		ConvID: &convID,
   489  	})
   490  	require.NoError(t, err)
   491  	require.Equal(t, chat1.MessageID(2), res[0].Conv.ReaderInfo.MaxMsgid, "wrong max msgid")
   492  	require.Equal(t, chat1.MessageID(2), res[0].Conv.ReaderInfo.ReadMsgid, "wrong read msgid")
   493  	require.Equal(t, gregor1.Time(0), res[0].Conv.ReaderInfo.LastSendTime)
   494  
   495  	err = inbox.ReadMessage(context.TODO(), uid, 10, conv.GetConvID(), 3)
   496  	require.IsType(t, VersionMismatchError{}, err)
   497  }
   498  
   499  func TestInboxSetStatus(t *testing.T) {
   500  
   501  	tc, inbox, uid := setupInboxTest(t, "basic")
   502  	defer tc.Cleanup()
   503  
   504  	// Create an inbox with a bunch of convos, merge it and read it back out
   505  	numConvs := 10
   506  	var convs []types.RemoteConversation
   507  	for i := numConvs - 1; i >= 0; i-- {
   508  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   509  	}
   510  
   511  	conv := convs[5]
   512  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   513  	require.NoError(t, inbox.SetStatus(context.TODO(), uid, 2, conv.GetConvID(),
   514  		chat1.ConversationStatus_IGNORED))
   515  
   516  	q := chat1.GetInboxQuery{
   517  		Status: []chat1.ConversationStatus{chat1.ConversationStatus_IGNORED},
   518  	}
   519  	require.NoError(t, inbox.Merge(context.TODO(), uid, 2, []chat1.Conversation{}, &q))
   520  	_, res, err := inbox.Read(context.TODO(), uid, &q)
   521  	require.NoError(t, err)
   522  	require.Equal(t, 1, len(res), "length")
   523  	require.Equal(t, conv.GetConvID(), res[0].GetConvID(), "id")
   524  
   525  	t.Logf("sending new message to wake up conv")
   526  	msg := makeInboxMsg(3, chat1.MessageType_TEXT)
   527  	msg.ClientHeader.Sender = uid
   528  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 3, conv.GetConvID(), msg, nil))
   529  	_, res, err = inbox.Read(context.TODO(), uid, &q)
   530  	require.NoError(t, err)
   531  	require.Equal(t, 0, len(res), "ignore not unset")
   532  
   533  	err = inbox.SetStatus(context.TODO(), uid, 10, conv.GetConvID(), chat1.ConversationStatus_BLOCKED)
   534  	require.IsType(t, VersionMismatchError{}, err)
   535  }
   536  
   537  func TestInboxSetStatusMuted(t *testing.T) {
   538  
   539  	tc, inbox, uid := setupInboxTest(t, "basic")
   540  	defer tc.Cleanup()
   541  
   542  	// Create an inbox with a bunch of convos, merge it and read it back out
   543  	numConvs := 10
   544  	var convs []types.RemoteConversation
   545  	for i := numConvs - 1; i >= 0; i-- {
   546  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   547  	}
   548  
   549  	conv := convs[5]
   550  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   551  	require.NoError(t, inbox.SetStatus(context.TODO(), uid, 2, conv.GetConvID(),
   552  		chat1.ConversationStatus_MUTED))
   553  
   554  	q := chat1.GetInboxQuery{
   555  		Status: []chat1.ConversationStatus{chat1.ConversationStatus_MUTED},
   556  	}
   557  	require.NoError(t, inbox.Merge(context.TODO(), uid, 2, []chat1.Conversation{}, &q))
   558  	_, res, err := inbox.Read(context.TODO(), uid, &q)
   559  	require.NoError(t, err)
   560  	require.Equal(t, 1, len(res), "length")
   561  	require.Equal(t, conv.GetConvID(), res[0].GetConvID(), "id")
   562  
   563  	t.Logf("sending new message to wake up conv")
   564  	msg := makeInboxMsg(3, chat1.MessageType_TEXT)
   565  	msg.ClientHeader.Sender = uid
   566  	require.NoError(t, inbox.NewMessage(context.TODO(), uid, 3, conv.GetConvID(), msg, nil))
   567  	_, res, err = inbox.Read(context.TODO(), uid, &q)
   568  	require.NoError(t, err)
   569  	require.Equal(t, 1, len(res), "muted wrongly unset")
   570  
   571  	err = inbox.SetStatus(context.TODO(), uid, 10, conv.GetConvID(), chat1.ConversationStatus_BLOCKED)
   572  	require.IsType(t, VersionMismatchError{}, err)
   573  }
   574  
   575  func TestInboxTlfFinalize(t *testing.T) {
   576  
   577  	tc, inbox, uid := setupInboxTest(t, "basic")
   578  	defer tc.Cleanup()
   579  
   580  	// Create an inbox with a bunch of convos, merge it and read it back out
   581  	numConvs := 10
   582  	var convs []types.RemoteConversation
   583  	for i := numConvs - 1; i >= 0; i-- {
   584  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   585  	}
   586  
   587  	conv := convs[5]
   588  	convID := conv.GetConvID()
   589  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   590  	require.NoError(t, inbox.TlfFinalize(context.TODO(), uid, 2, []chat1.ConversationID{conv.GetConvID()},
   591  		chat1.ConversationFinalizeInfo{ResetFull: "reset"}))
   592  	_, res, err := inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   593  		ConvID: &convID,
   594  	})
   595  	require.NoError(t, err)
   596  	require.Equal(t, 1, len(res), "length")
   597  	require.Equal(t, conv.GetConvID(), res[0].GetConvID(), "id")
   598  	require.NotNil(t, res[0].Conv.Metadata.FinalizeInfo, "finalize info")
   599  
   600  	err = inbox.TlfFinalize(context.TODO(), uid, 10, []chat1.ConversationID{conv.GetConvID()},
   601  		chat1.ConversationFinalizeInfo{ResetFull: "reset"})
   602  	require.IsType(t, VersionMismatchError{}, err)
   603  }
   604  
   605  func TestInboxSync(t *testing.T) {
   606  	tc, inbox, uid := setupInboxTest(t, "basic")
   607  	defer tc.Cleanup()
   608  
   609  	// Create an inbox with a bunch of convos, merge it and read it back out
   610  	numConvs := 10
   611  	var convs []types.RemoteConversation
   612  	for i := numConvs - 1; i >= 0; i-- {
   613  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   614  	}
   615  
   616  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   617  	_, res, err := inbox.Read(context.TODO(), uid, nil)
   618  	require.NoError(t, err)
   619  
   620  	var syncConvs []chat1.Conversation
   621  	convs[0].Conv.Metadata.Status = chat1.ConversationStatus_MUTED
   622  	convs[6].Conv.Metadata.Status = chat1.ConversationStatus_MUTED
   623  	syncConvs = append(syncConvs, convs[0].Conv)
   624  	syncConvs = append(syncConvs, convs[6].Conv)
   625  	newConv := makeConvo(gregor1.Time(60), 1, 1)
   626  	syncConvs = append(syncConvs, newConv.Conv)
   627  
   628  	vers, err := inbox.Version(context.TODO(), uid)
   629  	require.NoError(t, err)
   630  	syncRes, err := inbox.Sync(context.TODO(), uid, vers+1, syncConvs)
   631  	require.NoError(t, err)
   632  	newVers, newRes, err := inbox.Read(context.TODO(), uid, nil)
   633  	require.NoError(t, err)
   634  	sort.Sort(ByDatabaseOrder(newRes))
   635  	require.Equal(t, vers+1, newVers)
   636  	require.Equal(t, len(res)+1, len(newRes))
   637  	require.Equal(t, newConv.GetConvID(), newRes[0].GetConvID())
   638  	require.Equal(t, chat1.ConversationStatus_MUTED, newRes[1].Conv.Metadata.Status)
   639  	require.Equal(t, chat1.ConversationStatus_MUTED, newRes[7].Conv.Metadata.Status)
   640  	require.Equal(t, chat1.ConversationStatus_UNFILED, newRes[4].Conv.Metadata.Status)
   641  	require.False(t, syncRes.TeamTypeChanged)
   642  	require.Len(t, syncRes.Expunges, 0)
   643  
   644  	syncConvs = nil
   645  	vers, err = inbox.Version(context.TODO(), uid)
   646  	require.NoError(t, err)
   647  	convs[8].Conv.Metadata.TeamType = chat1.TeamType_COMPLEX
   648  	syncConvs = append(syncConvs, convs[8].Conv)
   649  	convs[9].Conv.Expunge = chat1.Expunge{Upto: 3}
   650  	syncConvs = append(syncConvs, convs[9].Conv)
   651  	syncRes, err = inbox.Sync(context.TODO(), uid, vers+1, syncConvs)
   652  	require.NoError(t, err)
   653  	newVers, newRes, err = inbox.Read(context.TODO(), uid, nil)
   654  	require.NoError(t, err)
   655  	sort.Sort(ByDatabaseOrder(newRes))
   656  	require.Equal(t, vers+1, newVers)
   657  	require.Equal(t, chat1.TeamType_COMPLEX, newRes[9].Conv.Metadata.TeamType)
   658  	require.True(t, syncRes.TeamTypeChanged)
   659  	require.Len(t, syncRes.Expunges, 1)
   660  	require.True(t, convs[9].Conv.GetConvID().Eq(syncRes.Expunges[0].ConvID))
   661  	require.Equal(t, convs[9].Conv.Expunge, syncRes.Expunges[0].Expunge)
   662  }
   663  
   664  func TestInboxServerVersion(t *testing.T) {
   665  	tc, inbox, uid := setupInboxTest(t, "basic")
   666  	defer tc.Cleanup()
   667  
   668  	// Create an inbox with a bunch of convos, merge it and read it back out
   669  	numConvs := 10
   670  	var convs []types.RemoteConversation
   671  	for i := numConvs - 1; i >= 0; i-- {
   672  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   673  	}
   674  
   675  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   676  	_, res, err := inbox.Read(context.TODO(), uid, nil)
   677  	require.NoError(t, err)
   678  	require.Equal(t, numConvs, len(res))
   679  
   680  	// Increase server version
   681  	cerr := tc.Context().ServerCacheVersions.Set(context.TODO(), chat1.ServerCacheVers{
   682  		InboxVers: 5,
   683  	})
   684  	require.NoError(t, cerr)
   685  
   686  	_, _, err = inbox.Read(context.TODO(), uid, nil)
   687  	require.Error(t, err)
   688  	require.IsType(t, MissError{}, err)
   689  
   690  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   691  	idata, err := inbox.readDiskVersions(context.TODO(), uid, true)
   692  	require.NoError(t, err)
   693  	require.Equal(t, 5, idata.ServerVersion)
   694  }
   695  
   696  func TestInboxKBFSUpgrade(t *testing.T) {
   697  	tc, inbox, uid := setupInboxTest(t, "kbfs")
   698  	defer tc.Cleanup()
   699  	numConvs := 10
   700  	var convs []types.RemoteConversation
   701  	for i := numConvs - 1; i >= 0; i-- {
   702  		convs = append(convs, makeConvo(gregor1.Time(i), 1, 1))
   703  	}
   704  	conv := convs[5]
   705  	convID := conv.GetConvID()
   706  	require.Equal(t, chat1.ConversationMembersType_KBFS, conv.Conv.GetMembersType())
   707  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   708  	require.NoError(t, inbox.UpgradeKBFSToImpteam(context.TODO(), uid, 2, conv.GetConvID()))
   709  	_, res, err := inbox.Read(context.TODO(), uid, &chat1.GetInboxQuery{
   710  		ConvID: &convID,
   711  	})
   712  	require.NoError(t, err)
   713  	require.Equal(t, 1, len(res), "length")
   714  	require.Equal(t, conv.GetConvID(), res[0].GetConvID(), "id")
   715  	require.Equal(t, chat1.ConversationMembersType_IMPTEAMUPGRADE, res[0].Conv.Metadata.MembersType)
   716  }
   717  
   718  func makeUID(t *testing.T) gregor1.UID {
   719  	b, err := libkb.RandBytes(16)
   720  	require.NoError(t, err)
   721  	return gregor1.UID(b)
   722  }
   723  
   724  func TestInboxMembershipDupUpdate(t *testing.T) {
   725  	ctc, inbox, uid := setupInboxTest(t, "membership")
   726  	defer ctc.Cleanup()
   727  
   728  	uid2 := makeUID(t)
   729  	conv := makeConvo(gregor1.Time(1), 1, 1)
   730  	conv.Conv.Metadata.AllList = []gregor1.UID{uid, uid2}
   731  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, []chat1.Conversation{conv.Conv}, nil))
   732  
   733  	otherJoinedConvs := []chat1.ConversationMember{{
   734  		Uid:    uid2,
   735  		ConvID: conv.GetConvID(),
   736  	}}
   737  	roleUpdates, err := inbox.MembershipUpdate(context.TODO(), uid, 2, []chat1.Conversation{conv.Conv},
   738  		nil, otherJoinedConvs, nil, nil, nil, nil)
   739  	require.NoError(t, err)
   740  	require.Nil(t, roleUpdates)
   741  
   742  	_, res, err := inbox.ReadAll(context.TODO(), uid, true)
   743  	require.NoError(t, err)
   744  	require.Equal(t, 1, len(res))
   745  	require.Equal(t, 2, len(res[0].Conv.Metadata.AllList))
   746  }
   747  
   748  func TestInboxMembershipUpdate(t *testing.T) {
   749  	ctc, inbox, uid := setupInboxTest(t, "membership")
   750  	defer ctc.Cleanup()
   751  
   752  	u2, err := kbtest.CreateAndSignupFakeUser("ib", ctc.G)
   753  	require.NoError(t, err)
   754  	uid2 := gregor1.UID(u2.User.GetUID().ToBytes())
   755  
   756  	u3, err := kbtest.CreateAndSignupFakeUser("ib", ctc.G)
   757  	require.NoError(t, err)
   758  	uid3 := gregor1.UID(u3.User.GetUID().ToBytes())
   759  
   760  	u4, err := kbtest.CreateAndSignupFakeUser("ib", ctc.G)
   761  	require.NoError(t, err)
   762  	uid4 := gregor1.UID(u4.User.GetUID().ToBytes())
   763  
   764  	t.Logf("uid: %s uid2: %s uid3: %s uid4: %s", uid, uid2, uid3, uid4)
   765  
   766  	// Create an inbox with a bunch of convos, merge it and read it back out
   767  	numConvs := 10
   768  	var convs []types.RemoteConversation
   769  	tlfID := makeTlfID()
   770  	for i := numConvs - 1; i >= 0; i-- {
   771  		conv := makeConvo(gregor1.Time(i), 1, 1)
   772  		conv.Conv.Metadata.IdTriple.Tlfid = tlfID
   773  		conv.Conv.Metadata.AllList = []gregor1.UID{uid, uid3, uid4}
   774  		convs = append(convs, conv)
   775  	}
   776  
   777  	require.NoError(t, inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil))
   778  	var joinedConvs []types.RemoteConversation
   779  	numJoinedConvs := 5
   780  	for i := 0; i < numJoinedConvs; i++ {
   781  		conv := makeConvo(gregor1.Time(i), 1, 1)
   782  		conv.Conv.Metadata.IdTriple.Tlfid = tlfID
   783  		conv.Conv.Metadata.AllList = []gregor1.UID{uid, uid3, uid4}
   784  		joinedConvs = append(joinedConvs, conv)
   785  	}
   786  
   787  	otherJoinConvID := convs[0].GetConvID()
   788  	otherJoinedConvs := []chat1.ConversationMember{{
   789  		Uid:    uid2,
   790  		ConvID: otherJoinConvID,
   791  	}}
   792  	otherRemovedConvID := convs[1].GetConvID()
   793  	otherRemovedConvs := []chat1.ConversationMember{{
   794  		Uid:    uid3,
   795  		ConvID: otherRemovedConvID,
   796  	}}
   797  	otherResetConvID := convs[2].GetConvID()
   798  	otherResetConvs := []chat1.ConversationMember{{
   799  		Uid:    uid4,
   800  		ConvID: otherResetConvID,
   801  	}}
   802  	userRemovedConvID := convs[5].GetConvID()
   803  	userRemovedConvs := []chat1.ConversationMember{{
   804  		Uid:    uid,
   805  		ConvID: userRemovedConvID,
   806  	}}
   807  	userResetConvID := convs[6].GetConvID()
   808  	userResetConvs := []chat1.ConversationMember{{
   809  		Uid:    uid,
   810  		ConvID: userResetConvID,
   811  	}}
   812  
   813  	roleUpdates, err := inbox.MembershipUpdate(context.TODO(), uid, 2, utils.PluckConvs(joinedConvs),
   814  		userRemovedConvs, otherJoinedConvs, otherRemovedConvs,
   815  		userResetConvs, otherResetConvs, &chat1.TeamMemberRoleUpdate{
   816  			TlfID: tlfID,
   817  			Role:  keybase1.TeamRole_WRITER,
   818  		})
   819  	require.NoError(t, err)
   820  	require.NotNil(t, roleUpdates)
   821  
   822  	vers, res, err := inbox.ReadAll(context.TODO(), uid, true)
   823  	require.NoError(t, err)
   824  	require.Equal(t, chat1.InboxVers(2), vers)
   825  	for i, c := range res {
   826  		// make sure we bump the local version during the membership update for a role change
   827  		require.EqualValues(t, 1, c.Conv.Metadata.LocalVersion)
   828  		res[i].Conv.Metadata.LocalVersion = 0 // zero it out for later equality checks
   829  		if c.GetConvID().Eq(convs[5].GetConvID()) {
   830  			require.Equal(t, chat1.ConversationMemberStatus_LEFT, c.Conv.ReaderInfo.Status)
   831  			require.Equal(t, keybase1.TeamRole_WRITER, c.Conv.ReaderInfo.UntrustedTeamRole)
   832  			convs[5].Conv.ReaderInfo.Status = chat1.ConversationMemberStatus_LEFT
   833  			convs[5].Conv.Metadata.Version = chat1.ConversationVers(2)
   834  		} else if c.GetConvID().Eq(convs[6].GetConvID()) {
   835  			require.Equal(t, chat1.ConversationMemberStatus_RESET, c.Conv.ReaderInfo.Status)
   836  			require.Equal(t, keybase1.TeamRole_WRITER, c.Conv.ReaderInfo.UntrustedTeamRole)
   837  			convs[6].Conv.ReaderInfo.Status = chat1.ConversationMemberStatus_RESET
   838  			convs[6].Conv.Metadata.Version = chat1.ConversationVers(2)
   839  		}
   840  	}
   841  	expected := convs
   842  	expected = append(expected, joinedConvs...)
   843  	sort.Sort(utils.RemoteConvByConvID(expected))
   844  	sort.Sort(utils.ByConvID(roleUpdates))
   845  	sort.Sort(utils.RemoteConvByConvID(res))
   846  	require.Equal(t, len(expected), len(res))
   847  	for i := 0; i < len(res); i++ {
   848  		sort.Sort(chat1.ByUID(res[i].Conv.Metadata.AllList))
   849  		sort.Sort(chat1.ByUID(expected[i].Conv.Metadata.AllList))
   850  		require.Equal(t, keybase1.TeamRole_WRITER, res[i].Conv.ReaderInfo.UntrustedTeamRole)
   851  		require.True(t, expected[i].GetConvID().Eq(roleUpdates[i]))
   852  		if res[i].GetConvID().Eq(otherJoinConvID) {
   853  			allUsers := []gregor1.UID{uid, uid2, uid3, uid4}
   854  			sort.Sort(chat1.ByUID(allUsers))
   855  			require.Equal(t, allUsers, res[i].Conv.Metadata.AllList)
   856  			require.Zero(t, len(res[i].Conv.Metadata.ResetList))
   857  		} else if res[i].GetConvID().Eq(otherRemovedConvID) {
   858  			allUsers := []gregor1.UID{uid, uid4}
   859  			sort.Sort(chat1.ByUID(allUsers))
   860  			require.Equal(t, allUsers, res[i].Conv.Metadata.AllList)
   861  			require.Zero(t, len(res[i].Conv.Metadata.ResetList))
   862  		} else if res[i].GetConvID().Eq(otherResetConvID) {
   863  			allUsers := []gregor1.UID{uid, uid3, uid4}
   864  			sort.Sort(chat1.ByUID(allUsers))
   865  			resetUsers := []gregor1.UID{uid4}
   866  			require.Len(t, res[i].Conv.Metadata.AllList, len(allUsers))
   867  			require.Equal(t, allUsers, res[i].Conv.Metadata.AllList)
   868  			require.Equal(t, resetUsers, res[i].Conv.Metadata.ResetList)
   869  		} else if res[i].GetConvID().Eq(userRemovedConvID) {
   870  			allUsers := []gregor1.UID{uid3, uid4}
   871  			sort.Sort(chat1.ByUID(allUsers))
   872  			require.Len(t, res[i].Conv.Metadata.AllList, len(allUsers))
   873  			require.Equal(t, allUsers, res[i].Conv.Metadata.AllList)
   874  			require.Zero(t, len(res[i].Conv.Metadata.ResetList))
   875  		} else if res[i].GetConvID().Eq(userResetConvID) {
   876  			allUsers := []gregor1.UID{uid, uid3, uid4}
   877  			sort.Sort(chat1.ByUID(allUsers))
   878  			resetUsers := []gregor1.UID{uid}
   879  			require.Len(t, res[i].Conv.Metadata.AllList, len(allUsers))
   880  			require.Equal(t, allUsers, res[i].Conv.Metadata.AllList)
   881  			require.Equal(t, resetUsers, res[i].Conv.Metadata.ResetList)
   882  		} else {
   883  			allUsers := []gregor1.UID{uid, uid3, uid4}
   884  			sort.Sort(chat1.ByUID(allUsers))
   885  			require.Equal(t, allUsers, res[i].Conv.Metadata.AllList)
   886  			expected[i].Conv.ReaderInfo.UntrustedTeamRole = keybase1.TeamRole_WRITER
   887  			require.Equal(t, expected[i], res[i])
   888  		}
   889  	}
   890  }
   891  
   892  // TestInboxCacheOnLogout checks that calling OnLogout() clears the cache.
   893  func TestInboxCacheOnLogout(t *testing.T) {
   894  	uid := keybase1.MakeTestUID(3)
   895  	inboxMemCache.PutVersions(gregor1.UID(uid), &inboxDiskVersions{})
   896  	require.NotEmpty(t, len(inboxMemCache.versMap))
   897  	err := inboxMemCache.OnLogout(libkb.NewMetaContextTODO(nil))
   898  	require.NoError(t, err)
   899  	require.Nil(t, inboxMemCache.GetVersions(gregor1.UID(uid)))
   900  	require.Empty(t, len(inboxMemCache.versMap))
   901  }
   902  
   903  func TestUpdateLocalMtime(t *testing.T) {
   904  	tc, inbox, uid := setupInboxTest(t, "local conv")
   905  	defer tc.Cleanup()
   906  	convs := []types.RemoteConversation{
   907  		makeConvo(gregor1.Time(1), 1, 1),
   908  		makeConvo(gregor1.Time(0), 1, 1),
   909  	}
   910  	err := inbox.Merge(context.TODO(), uid, 1, utils.PluckConvs(convs), nil)
   911  	require.NoError(t, err)
   912  	mtime1 := gregor1.Time(5)
   913  	mtime2 := gregor1.Time(1)
   914  	err = inbox.UpdateLocalMtime(context.TODO(), uid, []chat1.LocalMtimeUpdate{
   915  		{
   916  			ConvID: convs[0].GetConvID(),
   917  			Mtime:  mtime1,
   918  		},
   919  		{
   920  			ConvID: convs[1].GetConvID(),
   921  			Mtime:  mtime2,
   922  		},
   923  	})
   924  	require.NoError(t, err)
   925  
   926  	diskIndex, err := inbox.readDiskIndex(context.TODO(), uid, true)
   927  	require.NoError(t, err)
   928  	convs = nil
   929  	for _, convID := range diskIndex.ConversationIDs {
   930  		conv, err := inbox.readConv(context.TODO(), uid, convID)
   931  		require.NoError(t, err)
   932  		convs = append(convs, conv)
   933  	}
   934  
   935  	sort.Slice(convs, func(i, j int) bool {
   936  		return convs[i].GetMtime() > convs[j].GetMtime()
   937  	})
   938  	require.Equal(t, mtime1, convs[0].GetMtime())
   939  	require.Equal(t, mtime2, convs[1].GetMtime())
   940  }