github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbtest/chat.go (about)

     1  package kbtest
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/rand"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"sort"
    11  	"strings"
    12  	"sync"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/keybase/client/go/badges"
    17  	"github.com/keybase/client/go/chat/pager"
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/keybase/client/go/chat/globals"
    21  	"github.com/keybase/client/go/chat/types"
    22  	"github.com/keybase/client/go/chat/utils"
    23  	"github.com/keybase/client/go/externalstest"
    24  	"github.com/keybase/client/go/libkb"
    25  	"github.com/keybase/client/go/protocol/chat1"
    26  	"github.com/keybase/client/go/protocol/gregor1"
    27  	"github.com/keybase/client/go/protocol/keybase1"
    28  	"github.com/keybase/clockwork"
    29  	context "golang.org/x/net/context"
    30  )
    31  
    32  type ChatTestContext struct {
    33  	libkb.TestContext
    34  	ChatG *globals.ChatContext
    35  }
    36  
    37  func NewMetaContextForTest(c ChatTestContext) libkb.MetaContext {
    38  	return libkb.NewMetaContextForTest(c.TestContext)
    39  }
    40  
    41  func (c ChatTestContext) Context() *globals.Context {
    42  	return globals.NewContext(c.G, c.ChatG)
    43  }
    44  
    45  func (c ChatTestContext) Cleanup() {
    46  	if c.ChatG.PushHandler != nil {
    47  		<-c.ChatG.PushHandler.Stop(context.TODO())
    48  	}
    49  	if c.ChatG.MessageDeliverer != nil {
    50  		<-c.ChatG.MessageDeliverer.Stop(context.TODO())
    51  	}
    52  	if c.ChatG.ConvLoader != nil {
    53  		<-c.ChatG.ConvLoader.Stop(context.TODO())
    54  	}
    55  	if c.ChatG.FetchRetrier != nil {
    56  		<-c.ChatG.FetchRetrier.Stop(context.TODO())
    57  	}
    58  	if c.ChatG.EphemeralPurger != nil {
    59  		<-c.ChatG.EphemeralPurger.Stop(context.TODO())
    60  	}
    61  	if c.ChatG.EphemeralTracker != nil {
    62  		<-c.ChatG.EphemeralTracker.Stop(context.TODO())
    63  	}
    64  	if c.ChatG.InboxSource != nil {
    65  		<-c.ChatG.InboxSource.Stop(context.TODO())
    66  	}
    67  	if c.ChatG.Indexer != nil {
    68  		<-c.ChatG.Indexer.Stop(context.TODO())
    69  	}
    70  	if c.ChatG.CoinFlipManager != nil {
    71  		<-c.ChatG.CoinFlipManager.Stop(context.TODO())
    72  	}
    73  	if c.ChatG.BotCommandManager != nil {
    74  		<-c.ChatG.BotCommandManager.Stop(context.TODO())
    75  	}
    76  	if c.ChatG.UIInboxLoader != nil {
    77  		<-c.ChatG.UIInboxLoader.Stop(context.TODO())
    78  	}
    79  	c.TestContext.Cleanup()
    80  }
    81  
    82  type ChatMockWorld struct {
    83  	Fc clockwork.FakeClock
    84  
    85  	T       testing.TB
    86  	Tcs     map[string]*ChatTestContext
    87  	TcsByID map[string]*ChatTestContext
    88  	Users   map[string]*FakeUser
    89  	tlfs    map[keybase1.CanonicalTlfName]chat1.TLFID
    90  	tlfKeys map[keybase1.CanonicalTlfName][]keybase1.CryptKey
    91  
    92  	// should always be sorted by newly updated conversation first
    93  	conversations []*chat1.Conversation
    94  
    95  	// each slice should always be sorted by message ID in desc, i.e. newest messages first
    96  	Msgs map[chat1.ConvIDStr][]*chat1.MessageBoxed
    97  }
    98  
    99  func NewChatMockWorld(t *testing.T, name string, numUsers int) (world *ChatMockWorld) {
   100  	world = &ChatMockWorld{
   101  		T:       t,
   102  		Fc:      clockwork.NewFakeClockAt(time.Now()),
   103  		Tcs:     make(map[string]*ChatTestContext),
   104  		TcsByID: make(map[string]*ChatTestContext),
   105  		Users:   make(map[string]*FakeUser),
   106  		tlfs:    make(map[keybase1.CanonicalTlfName]chat1.TLFID),
   107  		tlfKeys: make(map[keybase1.CanonicalTlfName][]keybase1.CryptKey),
   108  		Msgs:    make(map[chat1.ConvIDStr][]*chat1.MessageBoxed),
   109  	}
   110  	for i := 0; i < numUsers; i++ {
   111  		kbTc := externalstest.SetupTest(t, "chat_"+name, 0)
   112  		tc := ChatTestContext{
   113  			TestContext: kbTc,
   114  			ChatG:       &globals.ChatContext{},
   115  		}
   116  		tc.ChatG.Badger = badges.NewBadger(kbTc.G)
   117  		tc.G.SetClock(world.Fc)
   118  		u, err := CreateAndSignupFakeUser("chat", tc.G)
   119  		if err != nil {
   120  			require.NoError(t, err)
   121  		}
   122  		world.Users[u.Username] = u
   123  		world.Tcs[u.Username] = &tc
   124  		world.TcsByID[u.User.GetUID().String()] = &tc
   125  	}
   126  
   127  	world.Fc.Advance(time.Hour)
   128  	return world
   129  }
   130  
   131  func (w *ChatMockWorld) Cleanup() {
   132  	for _, tc := range w.Tcs {
   133  		tc.Cleanup()
   134  	}
   135  }
   136  
   137  func (w *ChatMockWorld) GetConversationByID(convID chat1.ConversationID) *chat1.Conversation {
   138  	for _, conv := range w.conversations {
   139  		if conv.Metadata.ConversationID.ConvIDStr() == convID.ConvIDStr() {
   140  			return conv
   141  		}
   142  	}
   143  	return nil
   144  }
   145  
   146  type ByUsername []*FakeUser
   147  
   148  func (m ByUsername) Len() int      { return len(m) }
   149  func (m ByUsername) Swap(i, j int) { m[i], m[j] = m[j], m[i] }
   150  func (m ByUsername) Less(i, j int) bool {
   151  	res := strings.Compare(m[i].Username, m[j].Username)
   152  	return res < 0
   153  }
   154  
   155  func (w *ChatMockWorld) GetUsers() (res []*FakeUser) {
   156  	for _, v := range w.Users {
   157  		res = append(res, v)
   158  	}
   159  	sort.Sort(ByUsername(res))
   160  	return res
   161  }
   162  
   163  func mustGetRandBytesWithControlledFirstByte(n int, controlled byte) (b []byte) {
   164  	b = make([]byte, n)
   165  	if nn, err := rand.Read(b); err != nil || n != nn {
   166  		panic("oops")
   167  	}
   168  	b[0] = controlled // in case we have a collision?
   169  	return b
   170  }
   171  
   172  func mustGetRandCryptKeys(controlled byte) []keybase1.CryptKey {
   173  	key := keybase1.CryptKey{
   174  		KeyGeneration: 1,
   175  	}
   176  	copy(key.Key[:], mustGetRandBytesWithControlledFirstByte(32, controlled))
   177  	return []keybase1.CryptKey{key}
   178  }
   179  
   180  type TlfMock struct {
   181  	sync.Mutex
   182  	world *ChatMockWorld
   183  }
   184  
   185  func NewTlfMock(world *ChatMockWorld) *TlfMock {
   186  	return &TlfMock{world: world}
   187  }
   188  
   189  func CanonicalTlfNameForTest(tlfName string) keybase1.CanonicalTlfName {
   190  	// very much simplified canonicalization.
   191  	// TODO: implement rest when we need it
   192  	var names []string
   193  	nameMap := make(map[string]bool)
   194  	rawNames := strings.Split(tlfName, ",")
   195  	for _, rn := range rawNames {
   196  		if nameMap[rn] {
   197  			continue
   198  		}
   199  		names = append(names, rn)
   200  		nameMap[rn] = true
   201  	}
   202  	sort.Strings(names)
   203  	return keybase1.CanonicalTlfName(strings.Join(names, ","))
   204  }
   205  
   206  func (m *TlfMock) newTLFID() chat1.TLFID {
   207  	suffix := byte(0x29)
   208  	idBytes, err := libkb.RandBytesWithSuffix(16, suffix)
   209  	if err != nil {
   210  		panic("RandBytes failed: " + err.Error())
   211  	}
   212  	return chat1.TLFID(idBytes)
   213  }
   214  
   215  func (m *TlfMock) getTlfID(cname keybase1.CanonicalTlfName) (keybase1.TLFID, error) {
   216  	m.Lock()
   217  	defer m.Unlock()
   218  	tlfID, ok := m.world.tlfs[cname]
   219  	if !ok {
   220  		for _, n := range strings.Split(string(cname), ",") {
   221  			if m.world.Users[n] == nil {
   222  				return "", fmt.Errorf("user %s not found", n)
   223  			}
   224  		}
   225  		tlfID = m.newTLFID()
   226  		m.world.tlfs[cname] = tlfID
   227  		m.world.tlfKeys[cname] = mustGetRandCryptKeys(byte(len(m.world.tlfKeys) + 1))
   228  	}
   229  	return keybase1.TLFID(hex.EncodeToString([]byte(tlfID))), nil
   230  }
   231  
   232  func (m *TlfMock) AllCryptKeys(ctx context.Context, tlfName string, public bool) (res types.AllCryptKeys, err error) {
   233  	cres, err := m.CryptKeys(ctx, tlfName)
   234  	if err != nil {
   235  		return res, err
   236  	}
   237  	res = types.NewAllCryptKeys()
   238  	for _, key := range cres.CryptKeys {
   239  		res[chat1.ConversationMembersType_KBFS] =
   240  			append(res[chat1.ConversationMembersType_KBFS], key)
   241  		res[chat1.ConversationMembersType_TEAM] =
   242  			append(res[chat1.ConversationMembersType_TEAM], key)
   243  	}
   244  	return res, nil
   245  }
   246  func (m *TlfMock) LookupName(ctx context.Context, tlfID chat1.TLFID, public bool, tlfName string) (res types.NameInfo, err error) {
   247  	return m.LookupID(ctx, tlfName, public)
   248  }
   249  
   250  func (m *TlfMock) TeamBotSettings(ctx context.Context, tlfName string, tlfID chat1.TLFID,
   251  	membersType chat1.ConversationMembersType, public bool) (map[keybase1.UserVersion]keybase1.TeamBotSettings, error) {
   252  	return make(map[keybase1.UserVersion]keybase1.TeamBotSettings), nil
   253  }
   254  
   255  func (m *TlfMock) LookupID(ctx context.Context, tlfName string, public bool) (res types.NameInfo, err error) {
   256  	var tlfID keybase1.TLFID
   257  	name := CanonicalTlfNameForTest(tlfName)
   258  	res.CanonicalName = name.String()
   259  	if tlfID, err = m.getTlfID(name); err != nil {
   260  		return res, err
   261  	}
   262  	res.ID = tlfID.ToBytes()
   263  	return res, nil
   264  }
   265  
   266  func (m *TlfMock) EncryptionKey(ctx context.Context, tlfName string, tlfID chat1.TLFID,
   267  	membersType chat1.ConversationMembersType, public bool,
   268  	botUID *gregor1.UID) (key types.CryptKey, ni types.NameInfo, err error) {
   269  	if botUID != nil {
   270  		return key, ni, fmt.Errorf("TeambotKeys not supported by KBFS")
   271  	}
   272  	if ni, err = m.LookupID(ctx, tlfName, public); err != nil {
   273  		return key, ni, err
   274  	}
   275  	if public {
   276  		var zero [libkb.NaclDHKeySecretSize]byte
   277  		return keybase1.CryptKey{
   278  			KeyGeneration: 1,
   279  			Key:           keybase1.Bytes32(zero),
   280  		}, ni, nil
   281  	}
   282  	allKeys, err := m.AllCryptKeys(ctx, tlfName, public)
   283  	if err != nil {
   284  		return key, ni, err
   285  	}
   286  	keys := allKeys[chat1.ConversationMembersType_KBFS]
   287  	return keys[len(keys)-1], ni, nil
   288  }
   289  
   290  func (m *TlfMock) DecryptionKey(ctx context.Context, tlfName string, tlfID chat1.TLFID,
   291  	membersType chat1.ConversationMembersType, public bool,
   292  	keyGeneration int, kbfsEncrypted bool, botUID *gregor1.UID) (types.CryptKey, error) {
   293  	if botUID != nil {
   294  		return nil, fmt.Errorf("TeambotKeys not supported by KBFS")
   295  	}
   296  	if public {
   297  		var zero [libkb.NaclDHKeySecretSize]byte
   298  		return keybase1.CryptKey{
   299  			KeyGeneration: 1,
   300  			Key:           keybase1.Bytes32(zero),
   301  		}, nil
   302  	}
   303  	allkeys, err := m.AllCryptKeys(ctx, tlfName, public)
   304  	if err != nil {
   305  		return nil, err
   306  	}
   307  	keys := allkeys[chat1.ConversationMembersType_KBFS]
   308  	for _, key := range keys {
   309  		if key.Generation() == keyGeneration {
   310  			return key, nil
   311  		}
   312  	}
   313  	return nil, errors.New("no mock key found")
   314  }
   315  
   316  func (m *TlfMock) EphemeralEncryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
   317  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID) (types.EphemeralCryptKey, error) {
   318  	// Returns a totally zero teamEK. That's enough to get some very simple
   319  	// round trip tests to pass.
   320  	return keybase1.TeamEphemeralKey{}, nil
   321  }
   322  
   323  func (m *TlfMock) EphemeralDecryptionKey(mctx libkb.MetaContext, tlfName string, tlfID chat1.TLFID,
   324  	membersType chat1.ConversationMembersType, public bool, botUID *gregor1.UID,
   325  	generation keybase1.EkGeneration, contentCtime *gregor1.Time) (types.EphemeralCryptKey, error) {
   326  	// Returns a totally zero teamEK. That's enough to get some very simple
   327  	// round trip tests to pass.
   328  	return keybase1.TeamEphemeralKey{}, nil
   329  }
   330  
   331  func (m *TlfMock) ShouldPairwiseMAC(ctx context.Context, tlfName string, tlfID chat1.TLFID,
   332  	membersType chat1.ConversationMembersType, public bool) (bool, []keybase1.KID, error) {
   333  	return false, nil, nil
   334  }
   335  
   336  func (m *TlfMock) CryptKeys(ctx context.Context, tlfName string) (res keybase1.GetTLFCryptKeysRes, err error) {
   337  	res.NameIDBreaks.CanonicalName = CanonicalTlfNameForTest(tlfName)
   338  	if res.NameIDBreaks.TlfID, err = m.getTlfID(res.NameIDBreaks.CanonicalName); err != nil {
   339  		return keybase1.GetTLFCryptKeysRes{}, err
   340  	}
   341  	var ok bool
   342  	if res.CryptKeys, ok = m.world.tlfKeys[res.NameIDBreaks.CanonicalName]; !ok {
   343  		err = fmt.Errorf("CryptKeys for TLF %s not found", res.NameIDBreaks.CanonicalName)
   344  		return res, err
   345  	}
   346  	return res, nil
   347  }
   348  
   349  func (m *TlfMock) CompleteAndCanonicalizePrivateTlfName(ctx context.Context, tlfName string) (keybase1.CanonicalTLFNameAndIDWithBreaks, error) {
   350  	var res keybase1.CanonicalTLFNameAndIDWithBreaks
   351  	res.CanonicalName = CanonicalTlfNameForTest(tlfName)
   352  	var err error
   353  	res.TlfID, err = m.getTlfID(res.CanonicalName)
   354  	if err != nil {
   355  		return keybase1.CanonicalTLFNameAndIDWithBreaks{}, err
   356  	}
   357  	return res, nil
   358  }
   359  
   360  func (m *TlfMock) PublicCanonicalTLFNameAndID(ctx context.Context, tlfName string) (keybase1.CanonicalTLFNameAndIDWithBreaks, error) {
   361  	res := keybase1.CanonicalTLFNameAndIDWithBreaks{
   362  		CanonicalName: keybase1.CanonicalTlfName(tlfName),
   363  		TlfID:         "abcdefg",
   364  	}
   365  	return res, nil
   366  }
   367  
   368  type ChatRemoteMockServerConnection struct {
   369  	mock *ChatRemoteMock
   370  }
   371  
   372  func NewChatRemoteMockServerConnection(mock *ChatRemoteMock) *ChatRemoteMockServerConnection {
   373  	return &ChatRemoteMockServerConnection{
   374  		mock: mock,
   375  	}
   376  }
   377  
   378  func (m ChatRemoteMockServerConnection) GetClient() chat1.RemoteInterface {
   379  	return m.mock
   380  }
   381  
   382  func (m ChatRemoteMockServerConnection) Reconnect(ctx context.Context) (bool, error) {
   383  	return false, nil
   384  }
   385  
   386  type ChatRemoteMock struct {
   387  	world     *ChatMockWorld
   388  	readMsgid map[chat1.ConvIDStr]chat1.MessageID
   389  	uid       *gregor1.UID
   390  
   391  	CacheBodiesVersion int
   392  	CacheInboxVersion  int
   393  
   394  	GetThreadRemoteFunc func(m *ChatRemoteMock, ctx context.Context, arg chat1.GetThreadRemoteArg) (chat1.GetThreadRemoteRes, error)
   395  	SyncInboxFunc       func(m *ChatRemoteMock, ctx context.Context, vers chat1.InboxVers) (chat1.SyncInboxRes, error)
   396  }
   397  
   398  var _ chat1.RemoteInterface = (*ChatRemoteMock)(nil)
   399  
   400  func NewChatRemoteMock(world *ChatMockWorld) (m *ChatRemoteMock) {
   401  	m = &ChatRemoteMock{
   402  		world:     world,
   403  		readMsgid: make(map[chat1.ConvIDStr]chat1.MessageID),
   404  	}
   405  	return m
   406  }
   407  
   408  func (m *ChatRemoteMock) makeReaderInfo(convID chat1.ConversationID) (ri *chat1.ConversationReaderInfo) {
   409  	ri = &chat1.ConversationReaderInfo{}
   410  	convIDStr := convID.ConvIDStr()
   411  	ri.ReadMsgid = m.readMsgid[convIDStr]
   412  	for _, m := range m.world.Msgs[convIDStr] {
   413  		if m.ServerHeader.MessageID > ri.MaxMsgid {
   414  			ri.MaxMsgid = m.ServerHeader.MessageID
   415  			ri.Mtime = m.ServerHeader.Ctime
   416  		}
   417  	}
   418  	return ri
   419  }
   420  
   421  func (m *ChatRemoteMock) SetCurrentUser(uid gregor1.UID) {
   422  	m.uid = &uid
   423  }
   424  
   425  func (m *ChatRemoteMock) inConversation(conv *chat1.Conversation) bool {
   426  	if m.uid != nil && len(conv.Metadata.ActiveList) > 0 {
   427  		return conv.Includes(*m.uid)
   428  	}
   429  	return true
   430  }
   431  
   432  func (m *ChatRemoteMock) RemoteNotificationSuccessful(ctx context.Context, arg chat1.RemoteNotificationSuccessfulArg) error {
   433  	return nil
   434  }
   435  
   436  func (m *ChatRemoteMock) GetInboxRemote(ctx context.Context, arg chat1.GetInboxRemoteArg) (res chat1.GetInboxRemoteRes, err error) {
   437  	// TODO: add pagination support
   438  	var ibfull chat1.InboxViewFull
   439  	for _, conv := range m.world.conversations {
   440  		if !m.inConversation(conv) {
   441  			continue
   442  		}
   443  		if arg.Query != nil {
   444  			if arg.Query.ConvID != nil {
   445  				arg.Query.ConvIDs = append(arg.Query.ConvIDs, *arg.Query.ConvID)
   446  			}
   447  			if len(arg.Query.ConvIDs) > 0 {
   448  				found := false
   449  				for _, convID := range arg.Query.ConvIDs {
   450  					if convID.Eq(conv.GetConvID()) {
   451  						found = true
   452  						break
   453  					}
   454  				}
   455  				if !found {
   456  					continue
   457  				}
   458  			}
   459  			if arg.Query.ConvID != nil && !conv.Metadata.ConversationID.Eq(*arg.Query.ConvID) {
   460  				continue
   461  			}
   462  			if arg.Query.TlfID != nil && !conv.Metadata.IdTriple.Tlfid.Eq(*arg.Query.TlfID) {
   463  				continue
   464  			}
   465  			if arg.Query.TopicType != nil && conv.Metadata.IdTriple.TopicType != *arg.Query.TopicType {
   466  				continue
   467  			}
   468  			if arg.Query.UnreadOnly && m.readMsgid[conv.Metadata.ConversationID.ConvIDStr()] == m.makeReaderInfo(conv.Metadata.ConversationID).MaxMsgid {
   469  				continue
   470  			}
   471  			if arg.Query.ReadOnly && m.readMsgid[conv.Metadata.ConversationID.ConvIDStr()] != m.makeReaderInfo(conv.Metadata.ConversationID).MaxMsgid {
   472  				continue
   473  			}
   474  			if arg.Query.TlfVisibility != nil && conv.Metadata.Visibility != *arg.Query.TlfVisibility {
   475  				continue
   476  			}
   477  			if arg.Query.After != nil && m.makeReaderInfo(conv.Metadata.ConversationID).Mtime < *arg.Query.After {
   478  				continue
   479  			}
   480  			if arg.Query.Before != nil && m.makeReaderInfo(conv.Metadata.ConversationID).Mtime > *arg.Query.Before {
   481  				continue
   482  			}
   483  		}
   484  		convToAppend := *conv
   485  		convToAppend.ReaderInfo = m.makeReaderInfo(convToAppend.Metadata.ConversationID)
   486  		convToAppend.Notifications = new(chat1.ConversationNotificationInfo)
   487  
   488  		ibfull.Conversations = append(ibfull.Conversations, convToAppend)
   489  		if arg.Pagination != nil && arg.Pagination.Num != 0 && arg.Pagination.Num == len(ibfull.Conversations) {
   490  			break
   491  		}
   492  	}
   493  	return chat1.GetInboxRemoteRes{
   494  		Inbox: chat1.NewInboxViewWithFull(ibfull),
   495  	}, nil
   496  }
   497  
   498  func (m *ChatRemoteMock) GetPublicConversations(ctx context.Context, arg chat1.GetPublicConversationsArg) (res chat1.GetPublicConversationsRes, err error) {
   499  
   500  	for _, conv := range m.world.conversations {
   501  		if conv.Metadata.Visibility == keybase1.TLFVisibility_PUBLIC &&
   502  			conv.Metadata.IdTriple.Tlfid.Eq(arg.TlfID) &&
   503  			conv.Metadata.IdTriple.TopicType == arg.TopicType {
   504  
   505  			convToAppend := *conv
   506  			convToAppend.ReaderInfo = m.makeReaderInfo(convToAppend.Metadata.ConversationID)
   507  			res.Conversations = append(res.Conversations, convToAppend)
   508  
   509  		}
   510  	}
   511  
   512  	return res, nil
   513  }
   514  
   515  func (m *ChatRemoteMock) GetInboxByTLFIDRemote(ctx context.Context, tlfID chat1.TLFID) (res chat1.GetInboxByTLFIDRemoteRes, err error) {
   516  	for _, conv := range m.world.conversations {
   517  		if tlfID.Eq(conv.Metadata.IdTriple.Tlfid) {
   518  			convToAppend := *conv
   519  			convToAppend.ReaderInfo = m.makeReaderInfo(convToAppend.Metadata.ConversationID)
   520  			res.Convs = []chat1.Conversation{convToAppend}
   521  			return res, nil
   522  		}
   523  	}
   524  	return res, errors.New("conversation not found")
   525  }
   526  
   527  func (m *ChatRemoteMock) GetThreadRemote(ctx context.Context, arg chat1.GetThreadRemoteArg) (res chat1.GetThreadRemoteRes, err error) {
   528  	if m.GetThreadRemoteFunc != nil {
   529  		return m.GetThreadRemoteFunc(m, ctx, arg)
   530  	}
   531  	var mts map[chat1.MessageType]bool
   532  	if arg.Query != nil && len(arg.Query.MessageTypes) > 0 {
   533  		mts = make(map[chat1.MessageType]bool)
   534  		for _, mt := range arg.Query.MessageTypes {
   535  			mts[mt] = true
   536  		}
   537  	}
   538  
   539  	// TODO: add *real* pagination support
   540  	if arg.Pagination == nil {
   541  		arg.Pagination = &chat1.Pagination{Num: 10000}
   542  	}
   543  	conv, err := m.GetConversationMetadataRemote(ctx, arg.ConversationID)
   544  	if err != nil {
   545  		return res, err
   546  	}
   547  	res.MembersType = conv.Conv.GetMembersType()
   548  	res.Visibility = conv.Conv.Metadata.Visibility
   549  	msgs := m.world.Msgs[arg.ConversationID.ConvIDStr()]
   550  	count := 0
   551  	for _, msg := range msgs {
   552  		if arg.Query != nil {
   553  			if arg.Query.After != nil && msg.ServerHeader.Ctime < *arg.Query.After {
   554  				continue
   555  			}
   556  			if arg.Query.Before != nil && msg.ServerHeader.Ctime > *arg.Query.Before {
   557  				continue
   558  			}
   559  
   560  		}
   561  		res.Thread.Messages = append(res.Thread.Messages, *msg)
   562  		if mts != nil && mts[msg.GetMessageType()] {
   563  			count++
   564  		} else if mts == nil {
   565  			count++
   566  		}
   567  		if count >= arg.Pagination.Num {
   568  			break
   569  		}
   570  	}
   571  	if arg.Query != nil && arg.Query.MarkAsRead {
   572  		m.readMsgid[arg.ConversationID.ConvIDStr()] = msgs[0].ServerHeader.MessageID
   573  	}
   574  	var pmsgs []pager.Message
   575  	for _, m := range res.Thread.Messages {
   576  		pmsgs = append(pmsgs, m)
   577  	}
   578  	res.Thread.Pagination, err = pager.NewThreadPager().MakePage(pmsgs, arg.Pagination.Num, 0)
   579  	if err != nil {
   580  		return res, err
   581  	}
   582  	return res, nil
   583  }
   584  
   585  func (m *ChatRemoteMock) GetUnreadlineRemote(ctx context.Context, arg chat1.GetUnreadlineRemoteArg) (res chat1.GetUnreadlineRemoteRes, err error) {
   586  	return res, nil
   587  }
   588  
   589  func (m *ChatRemoteMock) GetConversationMetadataRemote(ctx context.Context, convID chat1.ConversationID) (res chat1.GetConversationMetadataRemoteRes, err error) {
   590  	conv := m.world.GetConversationByID(convID)
   591  	if conv == nil {
   592  		err = errors.New("conversation not found")
   593  		return res, err
   594  	}
   595  	res.Conv = *conv
   596  	return res, err
   597  }
   598  
   599  func (m *ChatRemoteMock) headerToVerifiedForTesting(h chat1.MessageClientHeader) chat1.MessageClientHeaderVerified {
   600  	return chat1.MessageClientHeaderVerified{
   601  		Conv:         h.Conv,
   602  		TlfName:      h.TlfName,
   603  		TlfPublic:    h.TlfPublic,
   604  		MessageType:  h.MessageType,
   605  		Prev:         h.Prev,
   606  		Sender:       h.Sender,
   607  		SenderDevice: h.SenderDevice,
   608  		OutboxID:     h.OutboxID,
   609  		OutboxInfo:   h.OutboxInfo,
   610  	}
   611  }
   612  
   613  func (m *ChatRemoteMock) promoteWriter(ctx context.Context, sender gregor1.UID, writers []gregor1.UID) []gregor1.UID {
   614  	res := make([]gregor1.UID, len(writers))
   615  	copy(res, writers)
   616  	for index, w := range writers {
   617  		if bytes.Equal(w.Bytes(), sender.Bytes()) {
   618  			res = append(res[:index], res[index+1:]...)
   619  			res = append([]gregor1.UID{sender}, res...)
   620  			return res
   621  		}
   622  	}
   623  	res = append([]gregor1.UID{sender}, res...)
   624  	return res
   625  }
   626  
   627  func (m *ChatRemoteMock) createBogusBody(typ chat1.MessageType) chat1.MessageBody {
   628  	return chat1.MessageBody{
   629  		MessageType__:        typ,
   630  		Text__:               &chat1.MessageText{},
   631  		Edit__:               &chat1.MessageEdit{},
   632  		Attachment__:         &chat1.MessageAttachment{},
   633  		Delete__:             &chat1.MessageDelete{},
   634  		Attachmentuploaded__: &chat1.MessageAttachmentUploaded{},
   635  		Join__:               &chat1.MessageJoin{},
   636  		Leave__:              &chat1.MessageLeave{},
   637  		Headline__:           &chat1.MessageHeadline{},
   638  		Metadata__:           &chat1.MessageConversationMetadata{},
   639  		Reaction__:           &chat1.MessageReaction{},
   640  	}
   641  }
   642  
   643  type dummyChannelSource struct{}
   644  
   645  var _ types.TeamChannelSource = (*dummyChannelSource)(nil)
   646  
   647  func (d dummyChannelSource) GetLastActiveForTLF(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID,
   648  	topicType chat1.TopicType) (gregor1.Time, error) {
   649  	return 0, nil
   650  }
   651  
   652  func (d dummyChannelSource) GetLastActiveForTeams(ctx context.Context, uid gregor1.UID,
   653  	topicType chat1.TopicType) (res chat1.LastActiveTimeAll, err error) {
   654  	return res, nil
   655  }
   656  
   657  func (d dummyChannelSource) GetChannelsFull(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID,
   658  	topicType chat1.TopicType) ([]chat1.ConversationLocal, error) {
   659  	return nil, nil
   660  }
   661  
   662  func (d dummyChannelSource) GetChannelsTopicName(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID,
   663  	topicType chat1.TopicType) ([]chat1.ChannelNameMention, error) {
   664  	return nil, nil
   665  }
   666  
   667  func (d dummyChannelSource) GetChannelTopicName(ctx context.Context, uid gregor1.UID, tlfID chat1.TLFID,
   668  	topicType chat1.TopicType, convID chat1.ConversationID) (string, error) {
   669  	return "", nil
   670  }
   671  
   672  func (d dummyChannelSource) GetRecentJoins(ctx context.Context, convID chat1.ConversationID, remoteClient chat1.RemoteInterface) (int, error) {
   673  	return 0, nil
   674  }
   675  
   676  func (d dummyChannelSource) GetLastActiveAt(ctx context.Context, teamID keybase1.TeamID, uid gregor1.UID, remoteClient chat1.RemoteInterface) (gregor1.Time, error) {
   677  	return 0, nil
   678  }
   679  
   680  func (d dummyChannelSource) OnDbNuke(mctx libkb.MetaContext) error {
   681  	return nil
   682  }
   683  
   684  func (d dummyChannelSource) OnLogout(mctx libkb.MetaContext) error {
   685  	return nil
   686  }
   687  
   688  func (m *ChatRemoteMock) PostRemote(ctx context.Context, arg chat1.PostRemoteArg) (res chat1.PostRemoteRes, err error) {
   689  	uid := arg.MessageBoxed.ClientHeader.Sender
   690  	conv := m.world.GetConversationByID(arg.ConversationID)
   691  	ri := m.makeReaderInfo(conv.Metadata.ConversationID)
   692  	inserted := m.insertMsgAndSort(arg.ConversationID, arg.MessageBoxed)
   693  	m.world.T.Logf("PostRemote(convid:%v, msgid:%v, %v)",
   694  		arg.ConversationID, inserted.ServerHeader.MessageID, arg.MessageBoxed.GetMessageType())
   695  	if ri.ReadMsgid == ri.MaxMsgid {
   696  		m.readMsgid[arg.ConversationID.ConvIDStr()] = inserted.ServerHeader.MessageID
   697  	}
   698  	conv.Metadata.ActiveList = m.promoteWriter(ctx, arg.MessageBoxed.ClientHeader.Sender,
   699  		conv.Metadata.ActiveList)
   700  	conv.MaxMsgs = m.getMaxMsgs(arg.ConversationID)
   701  	conv.MaxMsgSummaries = nil
   702  	for _, m := range conv.MaxMsgs {
   703  		conv.MaxMsgSummaries = append(conv.MaxMsgSummaries, m.Summary())
   704  	}
   705  	conv.Metadata.Version++
   706  	sort.Sort(convByNewlyUpdated{mock: m})
   707  	res.MsgHeader = *inserted.ServerHeader
   708  	res.RateLimit = &chat1.RateLimit{}
   709  
   710  	// hit notify router with new message
   711  	if m.world.TcsByID[uid.String()].ChatG.ActivityNotifier != nil {
   712  		activity := chat1.NewChatActivityWithIncomingMessage(chat1.IncomingMessage{
   713  			Message: utils.PresentMessageUnboxed(ctx, m.world.TcsByID[uid.String()].Context(),
   714  				chat1.NewMessageUnboxedWithValid(chat1.MessageUnboxedValid{
   715  					ClientHeader: m.headerToVerifiedForTesting(inserted.ClientHeader),
   716  					ServerHeader: *inserted.ServerHeader,
   717  					MessageBody:  m.createBogusBody(inserted.GetMessageType()),
   718  				}), uid, arg.ConversationID),
   719  		})
   720  		m.world.TcsByID[uid.String()].ChatG.ActivityNotifier.Activity(context.Background(),
   721  			uid, conv.GetTopicType(), &activity, chat1.ChatActivitySource_REMOTE)
   722  	}
   723  
   724  	return
   725  }
   726  
   727  func (m *ChatRemoteMock) NewConversationRemote(ctx context.Context, arg chat1.ConversationIDTriple) (res chat1.NewConversationRemoteRes, err error) {
   728  	return res, errors.New("not implemented anymore")
   729  }
   730  
   731  func (m *ChatRemoteMock) NewConversationRemote2(ctx context.Context, arg chat1.NewConversationRemote2Arg) (res chat1.NewConversationRemoteRes, err error) {
   732  	for _, conv := range m.world.conversations {
   733  		if conv.Metadata.IdTriple.Tlfid.Eq(arg.IdTriple.Tlfid) &&
   734  			conv.Metadata.IdTriple.TopicID.String() == arg.IdTriple.TopicID.String() &&
   735  			conv.Metadata.IdTriple.TopicType == arg.IdTriple.TopicType {
   736  			// Identical triple
   737  			return res, libkb.ChatConvExistsError{ConvID: conv.Metadata.ConversationID}
   738  		}
   739  		if arg.IdTriple.TopicType == chat1.TopicType_CHAT &&
   740  			arg.MembersType != chat1.ConversationMembersType_TEAM &&
   741  			conv.Metadata.IdTriple.Tlfid.Eq(arg.IdTriple.Tlfid) &&
   742  			conv.Metadata.IdTriple.TopicType == arg.IdTriple.TopicType {
   743  			// Existing CHAT conv
   744  			return res, libkb.ChatConvExistsError{ConvID: conv.Metadata.ConversationID}
   745  		}
   746  	}
   747  
   748  	res.ConvID = arg.IdTriple.ToConversationID([2]byte{0, 0})
   749  
   750  	first := m.insertMsgAndSort(res.ConvID, arg.TLFMessage)
   751  	vis := keybase1.TLFVisibility_PRIVATE
   752  	if arg.TLFMessage.ClientHeader.TlfPublic {
   753  		vis = keybase1.TLFVisibility_PUBLIC
   754  	}
   755  	m.world.conversations = append(m.world.conversations, &chat1.Conversation{
   756  		Metadata: chat1.ConversationMetadata{
   757  			IdTriple:       arg.IdTriple,
   758  			ConversationID: res.ConvID,
   759  			Visibility:     vis,
   760  			MembersType:    arg.MembersType,
   761  			Version:        1,
   762  		},
   763  		MaxMsgs:         []chat1.MessageBoxed{first},
   764  		MaxMsgSummaries: []chat1.MessageSummary{first.Summary()},
   765  	})
   766  	m.readMsgid[res.ConvID.ConvIDStr()] = first.ServerHeader.MessageID
   767  
   768  	sort.Sort(convByNewlyUpdated{mock: m})
   769  	return res, nil
   770  }
   771  
   772  func (m *ChatRemoteMock) GetMessagesRemote(ctx context.Context, arg chat1.GetMessagesRemoteArg) (res chat1.GetMessagesRemoteRes, err error) {
   773  	msgs, ok := m.world.Msgs[arg.ConversationID.ConvIDStr()]
   774  	if !ok {
   775  		return res, errors.New("conversation not found")
   776  	}
   777  	ids := make(map[chat1.MessageID]bool)
   778  	for _, id := range arg.MessageIDs {
   779  		ids[id] = true
   780  	}
   781  	for _, msg := range msgs {
   782  		id := msg.ServerHeader.MessageID
   783  		if ids[id] {
   784  			res.Msgs = append(res.Msgs, *msg)
   785  			delete(ids, id)
   786  		}
   787  	}
   788  	for id := range ids {
   789  		return res, fmt.Errorf("message %d is not found", id)
   790  	}
   791  	return res, nil
   792  }
   793  
   794  func (m *ChatRemoteMock) MarkAsRead(ctx context.Context, arg chat1.MarkAsReadArg) (res chat1.MarkAsReadRes, err error) {
   795  	conv := m.world.GetConversationByID(arg.ConversationID)
   796  	if conv == nil {
   797  		err = errors.New("conversation not found")
   798  		return res, err
   799  	}
   800  	m.readMsgid[conv.Metadata.ConversationID.ConvIDStr()] = arg.MsgID
   801  	return res, nil
   802  }
   803  
   804  func (m *ChatRemoteMock) SetConversationStatus(ctx context.Context, arg chat1.SetConversationStatusArg) (res chat1.SetConversationStatusRes, err error) {
   805  	return chat1.SetConversationStatusRes{}, errors.New("not implemented")
   806  }
   807  
   808  func (m *ChatRemoteMock) SetAppNotificationSettings(ctx context.Context,
   809  	arg chat1.SetAppNotificationSettingsArg) (res chat1.SetAppNotificationSettingsRes, err error) {
   810  	return res, errors.New("not implemented")
   811  }
   812  
   813  func (m *ChatRemoteMock) RetentionSweepConv(ctx context.Context, convID chat1.ConversationID) (res chat1.SweepRes, err error) {
   814  	return res, errors.New("not implemented")
   815  }
   816  
   817  func (m *ChatRemoteMock) UpgradeKBFSToImpteam(ctx context.Context, arg chat1.UpgradeKBFSToImpteamArg) error {
   818  	return errors.New("not implemented")
   819  }
   820  
   821  func (m *ChatRemoteMock) SetGlobalAppNotificationSettings(ctx context.Context,
   822  	arg chat1.GlobalAppNotificationSettings) error {
   823  	return errors.New("not implemented")
   824  }
   825  
   826  func (m *ChatRemoteMock) GetGlobalAppNotificationSettings(ctx context.Context) (chat1.GlobalAppNotificationSettings, error) {
   827  	return chat1.GlobalAppNotificationSettings{}, errors.New("not implemented")
   828  }
   829  
   830  func (m *ChatRemoteMock) TlfFinalize(ctx context.Context, arg chat1.TlfFinalizeArg) error {
   831  	return nil
   832  }
   833  
   834  func (m *ChatRemoteMock) TlfResolve(ctx context.Context, arg chat1.TlfResolveArg) error {
   835  	return nil
   836  }
   837  
   838  func (m *ChatRemoteMock) GetUnreadUpdateFull(ctx context.Context, inboxVers chat1.InboxVers) (chat1.UnreadUpdateFull, error) {
   839  	return chat1.UnreadUpdateFull{}, errors.New("not implemented")
   840  }
   841  
   842  func (m *ChatRemoteMock) GetInboxVersion(ctx context.Context, uid gregor1.UID) (chat1.InboxVers, error) {
   843  	return 1, nil
   844  }
   845  
   846  func (m *ChatRemoteMock) SyncInbox(ctx context.Context, vers chat1.InboxVers) (chat1.SyncInboxRes, error) {
   847  	if m.SyncInboxFunc == nil {
   848  		return chat1.NewSyncInboxResWithClear(), nil
   849  	}
   850  	return m.SyncInboxFunc(m, ctx, vers)
   851  }
   852  
   853  func (m *ChatRemoteMock) SyncChat(ctx context.Context, arg chat1.SyncChatArg) (chat1.SyncChatRes, error) {
   854  	if m.SyncInboxFunc == nil {
   855  		return chat1.SyncChatRes{
   856  			InboxRes: chat1.NewSyncInboxResWithClear(),
   857  		}, nil
   858  	}
   859  
   860  	iboxRes, err := m.SyncInboxFunc(m, ctx, arg.Vers)
   861  	if err != nil {
   862  		return chat1.SyncChatRes{}, err
   863  	}
   864  	return chat1.SyncChatRes{
   865  		InboxRes: iboxRes,
   866  		CacheVers: chat1.ServerCacheVers{
   867  			InboxVers:  m.CacheInboxVersion,
   868  			BodiesVers: m.CacheBodiesVersion,
   869  		},
   870  	}, nil
   871  }
   872  
   873  func (m *ChatRemoteMock) SyncAll(ctx context.Context, arg chat1.SyncAllArg) (res chat1.SyncAllResult, err error) {
   874  	cres, err := m.SyncChat(ctx, chat1.SyncChatArg{
   875  		Vers:             arg.InboxVers,
   876  		SummarizeMaxMsgs: arg.SummarizeMaxMsgs,
   877  	})
   878  	if err != nil {
   879  		return res, err
   880  	}
   881  	return chat1.SyncAllResult{
   882  		Chat: cres,
   883  	}, nil
   884  }
   885  
   886  func (m *ChatRemoteMock) UpdateTypingRemote(ctx context.Context, arg chat1.UpdateTypingRemoteArg) error {
   887  	return nil
   888  }
   889  
   890  func (m *ChatRemoteMock) GetTLFConversations(ctx context.Context, arg chat1.GetTLFConversationsArg) (chat1.GetTLFConversationsRes, error) {
   891  	return chat1.GetTLFConversationsRes{}, nil
   892  }
   893  
   894  func (m *ChatRemoteMock) JoinConversation(ctx context.Context, convID chat1.ConversationID) (chat1.JoinLeaveConversationRemoteRes, error) {
   895  	return chat1.JoinLeaveConversationRemoteRes{}, nil
   896  }
   897  
   898  func (m *ChatRemoteMock) LeaveConversation(ctx context.Context, convID chat1.ConversationID) (chat1.JoinLeaveConversationRemoteRes, error) {
   899  	return chat1.JoinLeaveConversationRemoteRes{}, nil
   900  }
   901  
   902  func (m *ChatRemoteMock) PreviewConversation(ctx context.Context, convID chat1.ConversationID) (chat1.JoinLeaveConversationRemoteRes, error) {
   903  	return chat1.JoinLeaveConversationRemoteRes{}, nil
   904  }
   905  
   906  func (m *ChatRemoteMock) DeleteConversation(ctx context.Context, convID chat1.ConversationID) (chat1.DeleteConversationRemoteRes, error) {
   907  	return chat1.DeleteConversationRemoteRes{}, nil
   908  }
   909  
   910  func (m *ChatRemoteMock) RemoveFromConversation(ctx context.Context, arg chat1.RemoveFromConversationArg) (chat1.RemoveFromConversationRemoteRes, error) {
   911  	return chat1.RemoveFromConversationRemoteRes{}, nil
   912  }
   913  
   914  func (m *ChatRemoteMock) GetMessageBefore(ctx context.Context, arg chat1.GetMessageBeforeArg) (chat1.GetMessageBeforeRes, error) {
   915  	// Ignore age and get the latest message
   916  	var latest chat1.MessageID
   917  	convID := arg.ConvID.ConvIDStr()
   918  	for _, msg := range m.world.Msgs[convID] {
   919  		if msg.ServerHeader.MessageID >= latest {
   920  			latest = msg.ServerHeader.MessageID
   921  		}
   922  	}
   923  	return chat1.GetMessageBeforeRes{MsgID: latest}, nil
   924  }
   925  
   926  type convByNewlyUpdated struct {
   927  	mock *ChatRemoteMock
   928  }
   929  
   930  func (s convByNewlyUpdated) Len() int { return len(s.mock.world.conversations) }
   931  func (s convByNewlyUpdated) Swap(i, j int) {
   932  	s.mock.world.conversations[i], s.mock.world.conversations[j] = s.mock.world.conversations[j], s.mock.world.conversations[i]
   933  }
   934  func (s convByNewlyUpdated) Less(i, j int) bool {
   935  	return s.mock.makeReaderInfo(s.mock.world.conversations[i].Metadata.ConversationID).Mtime > s.mock.makeReaderInfo(s.mock.world.conversations[j].Metadata.ConversationID).Mtime
   936  }
   937  
   938  type msgByMessageIDDesc struct {
   939  	world  *ChatMockWorld
   940  	convID chat1.ConversationID
   941  }
   942  
   943  func (s msgByMessageIDDesc) Len() int { return len(s.world.Msgs[s.convID.ConvIDStr()]) }
   944  func (s msgByMessageIDDesc) Swap(i, j int) {
   945  	convID := s.convID.ConvIDStr()
   946  	s.world.Msgs[convID][i], s.world.Msgs[convID][j] =
   947  		s.world.Msgs[convID][j], s.world.Msgs[convID][i]
   948  }
   949  func (s msgByMessageIDDesc) Less(i, j int) bool {
   950  	convID := s.convID.ConvIDStr()
   951  	return s.world.Msgs[convID][i].ServerHeader.MessageID > s.world.Msgs[convID][j].ServerHeader.MessageID
   952  }
   953  
   954  func (m *ChatRemoteMock) getMaxMsgs(convID chat1.ConversationID) (maxMsgs []chat1.MessageBoxed) {
   955  	finder := make(map[chat1.MessageType]*chat1.MessageBoxed)
   956  	convIDStr := convID.ConvIDStr()
   957  	for _, msg := range m.world.Msgs[convIDStr] {
   958  		if existing, ok := finder[msg.GetMessageType()]; !ok || existing.GetMessageID() < msg.GetMessageID() {
   959  			finder[msg.GetMessageType()] = msg
   960  		}
   961  	}
   962  
   963  	for _, msg := range finder {
   964  		maxMsgs = append(maxMsgs, *msg)
   965  	}
   966  
   967  	return maxMsgs
   968  }
   969  
   970  func (m *ChatRemoteMock) insertMsgAndSort(convID chat1.ConversationID, msg chat1.MessageBoxed) (inserted chat1.MessageBoxed) {
   971  	convIDStr := convID.ConvIDStr()
   972  	msg.ServerHeader = &chat1.MessageServerHeader{
   973  		Ctime:     gregor1.ToTime(m.world.Fc.Now()),
   974  		Now:       gregor1.ToTime(m.world.Fc.Now()),
   975  		MessageID: chat1.MessageID(len(m.world.Msgs[convIDStr]) + 1),
   976  	}
   977  	m.world.Msgs[convIDStr] = append(m.world.Msgs[convIDStr], &msg)
   978  	sort.Sort(msgByMessageIDDesc{world: m.world, convID: convID})
   979  
   980  	// If this message supersedes something, track it down and set supersededBy
   981  	if msg.ClientHeader.Supersedes > 0 {
   982  		for _, wmsg := range m.world.Msgs[convIDStr] {
   983  			if wmsg.GetMessageID() == msg.ClientHeader.Supersedes {
   984  				wmsg.ServerHeader.SupersededBy = msg.GetMessageID()
   985  			}
   986  		}
   987  	}
   988  
   989  	return msg
   990  }
   991  
   992  func (m *ChatRemoteMock) BroadcastGregorMessageToConv(ctx context.Context,
   993  	arg chat1.BroadcastGregorMessageToConvArg) error {
   994  	return nil
   995  }
   996  
   997  func (m *ChatRemoteMock) GetS3Params(context.Context, chat1.GetS3ParamsArg) (chat1.S3Params, error) {
   998  	return chat1.S3Params{}, errors.New("GetS3Params not mocked")
   999  }
  1000  
  1001  func (m *ChatRemoteMock) S3Sign(context.Context, chat1.S3SignArg) ([]byte, error) {
  1002  	return nil, errors.New("GetS3Params not mocked")
  1003  }
  1004  
  1005  func (m *ChatRemoteMock) SetConvRetention(ctx context.Context, _ chat1.SetConvRetentionArg) (res chat1.SetRetentionRes, err error) {
  1006  	return res, errors.New("SetConvRetention not mocked")
  1007  }
  1008  
  1009  func (m *ChatRemoteMock) SetTeamRetention(ctx context.Context, _ chat1.SetTeamRetentionArg) (res chat1.SetRetentionRes, err error) {
  1010  	return res, errors.New("SetTeamRetention not mocked")
  1011  }
  1012  
  1013  func (m *ChatRemoteMock) SetConvMinWriterRole(ctx context.Context, _ chat1.SetConvMinWriterRoleArg) (res chat1.SetConvMinWriterRoleRes, err error) {
  1014  	return res, errors.New("SetConvMinWriterRole not mocked")
  1015  }
  1016  
  1017  func (m *ChatRemoteMock) RegisterSharePost(ctx context.Context, _ chat1.RegisterSharePostArg) error {
  1018  	return errors.New("RegisterSharePost not mocked")
  1019  }
  1020  
  1021  func (m *ChatRemoteMock) FailSharePost(ctx context.Context, _ chat1.FailSharePostArg) error {
  1022  	return errors.New("FailSharePost not mocked")
  1023  }
  1024  
  1025  func (m *ChatRemoteMock) ServerNow(ctx context.Context) (res chat1.ServerNowRes, err error) {
  1026  	return res, errors.New("ServerNow not mocked")
  1027  }
  1028  
  1029  func (m *ChatRemoteMock) GetExternalAPIKeys(ctx context.Context, typs []chat1.ExternalAPIKeyTyp) (res []chat1.ExternalAPIKey, err error) {
  1030  	return res, errors.New("GetExternalAPIKeys not mocked")
  1031  }
  1032  
  1033  func (m *ChatRemoteMock) AdvertiseBotCommands(ctx context.Context, ads []chat1.RemoteBotCommandsAdvertisement) (res chat1.AdvertiseBotCommandsRes, err error) {
  1034  	return res, errors.New("AdvertiseBotCommands not mocked")
  1035  }
  1036  
  1037  func (m *ChatRemoteMock) ClearBotCommands(ctx context.Context, filter *chat1.RemoteClearBotCommandsFilter) (res chat1.ClearBotCommandsRes, err error) {
  1038  	return res, errors.New("ClearBotCommands not mocked")
  1039  }
  1040  
  1041  func (m *ChatRemoteMock) GetBotInfo(ctx context.Context, arg chat1.GetBotInfoArg) (res chat1.GetBotInfoRes, err error) {
  1042  	return res, errors.New("GetBotInfo not mocked")
  1043  }
  1044  
  1045  func (m *ChatRemoteMock) GetDefaultTeamChannels(ctx context.Context, teamID keybase1.TeamID) (res chat1.GetDefaultTeamChannelsRes, err error) {
  1046  	return res, errors.New("GetDefaultTeamChannels not mocked")
  1047  }
  1048  
  1049  func (m *ChatRemoteMock) SetDefaultTeamChannels(ctx context.Context, arg chat1.SetDefaultTeamChannelsArg) (res chat1.SetDefaultTeamChannelsRes, err error) {
  1050  	return res, errors.New("SetDefaultTeamChannels not mocked")
  1051  }
  1052  
  1053  func (m *ChatRemoteMock) GetRecentJoins(ctx context.Context, convID chat1.ConversationID) (res chat1.GetRecentJoinsRes, err error) {
  1054  	return res, errors.New("GetRecentJoins not mocked")
  1055  }
  1056  
  1057  func (m *ChatRemoteMock) GetLastActiveAt(ctx context.Context, arg chat1.GetLastActiveAtArg) (res chat1.GetLastActiveAtRes, err error) {
  1058  	return res, errors.New("GetLastActiveAt not mocked")
  1059  }
  1060  
  1061  func (m *ChatRemoteMock) GetResetConversations(ctx context.Context) (res chat1.GetResetConversationsRes, err error) {
  1062  	return res, errors.New("GetResetConversations not mocked")
  1063  }
  1064  
  1065  func (m *ChatRemoteMock) TeamIDOfConv(ctx context.Context, convID chat1.ConversationID) (res *keybase1.TeamID, err error) {
  1066  	return res, errors.New("TeamIDOfConv not mocked")
  1067  }
  1068  
  1069  func (m *ChatRemoteMock) RefreshParticipantsRemote(ctx context.Context,
  1070  	arg chat1.RefreshParticipantsRemoteArg) (res chat1.RefreshParticipantsRemoteRes, err error) {
  1071  	return res, errors.New("not implemented")
  1072  }
  1073  
  1074  type NonblockInboxResult struct {
  1075  	ConvID   chat1.ConversationID
  1076  	Err      error
  1077  	ConvRes  *chat1.InboxUIItem
  1078  	InboxRes *chat1.UnverifiedInboxUIItems
  1079  }
  1080  
  1081  type NonblockThreadResult struct {
  1082  	Thread *chat1.UIMessages
  1083  	Full   bool
  1084  }
  1085  
  1086  type NonblockSearchResult struct {
  1087  	chat1.ChatSearchHitArg
  1088  }
  1089  
  1090  type ChatUI struct {
  1091  	InboxCb               chan NonblockInboxResult
  1092  	ThreadCb              chan NonblockThreadResult
  1093  	ThreadStatusCb        chan chat1.UIChatThreadStatus
  1094  	SearchHitCb           chan chat1.ChatSearchHitArg
  1095  	SearchDoneCb          chan chat1.ChatSearchDoneArg
  1096  	InboxSearchHitCb      chan chat1.ChatSearchInboxHitArg
  1097  	InboxSearchDoneCb     chan chat1.ChatSearchInboxDoneArg
  1098  	InboxSearchConvHitsCb chan []chat1.UIChatSearchConvHit
  1099  	StellarShowConfirm    chan struct{}
  1100  	StellarDataConfirm    chan chat1.UIChatPaymentSummary
  1101  	StellarDataError      chan keybase1.Status
  1102  	StellarDone           chan struct{}
  1103  	ShowManageChannels    chan string
  1104  	GiphyResults          chan chat1.GiphySearchResults
  1105  	GiphyWindow           chan bool
  1106  	CoinFlipUpdates       chan []chat1.UICoinFlipStatus
  1107  	CommandMarkdown       chan *chat1.UICommandMarkdown
  1108  	InboxLayoutCb         chan chat1.UIInboxLayout
  1109  }
  1110  
  1111  func NewChatUI() *ChatUI {
  1112  	return &ChatUI{
  1113  		InboxCb:               make(chan NonblockInboxResult, 50),
  1114  		ThreadCb:              make(chan NonblockThreadResult, 50),
  1115  		ThreadStatusCb:        make(chan chat1.UIChatThreadStatus, 50),
  1116  		SearchHitCb:           make(chan chat1.ChatSearchHitArg, 50),
  1117  		SearchDoneCb:          make(chan chat1.ChatSearchDoneArg, 50),
  1118  		InboxSearchHitCb:      make(chan chat1.ChatSearchInboxHitArg, 50),
  1119  		InboxSearchDoneCb:     make(chan chat1.ChatSearchInboxDoneArg, 50),
  1120  		InboxSearchConvHitsCb: make(chan []chat1.UIChatSearchConvHit, 50),
  1121  		StellarShowConfirm:    make(chan struct{}, 10),
  1122  		StellarDataConfirm:    make(chan chat1.UIChatPaymentSummary, 10),
  1123  		StellarDataError:      make(chan keybase1.Status, 10),
  1124  		StellarDone:           make(chan struct{}, 10),
  1125  		ShowManageChannels:    make(chan string, 10),
  1126  		GiphyResults:          make(chan chat1.GiphySearchResults, 10),
  1127  		GiphyWindow:           make(chan bool, 10),
  1128  		CoinFlipUpdates:       make(chan []chat1.UICoinFlipStatus, 100),
  1129  		CommandMarkdown:       make(chan *chat1.UICommandMarkdown, 10),
  1130  		InboxLayoutCb:         make(chan chat1.UIInboxLayout, 200),
  1131  	}
  1132  }
  1133  
  1134  func (c *ChatUI) ChatInboxConversation(ctx context.Context, arg chat1.ChatInboxConversationArg) error {
  1135  	var inboxItems []chat1.InboxUIItem
  1136  	if err := json.Unmarshal([]byte(arg.Convs), &inboxItems); err != nil {
  1137  		return err
  1138  	}
  1139  	for _, inboxItem := range inboxItems {
  1140  		c.InboxCb <- NonblockInboxResult{
  1141  			ConvRes: &inboxItem,
  1142  			ConvID:  inboxItem.GetConvID(),
  1143  		}
  1144  	}
  1145  	return nil
  1146  }
  1147  
  1148  func (c *ChatUI) ChatInboxLayout(ctx context.Context, layout string) error {
  1149  	var uilayout chat1.UIInboxLayout
  1150  	if err := json.Unmarshal([]byte(layout), &uilayout); err != nil {
  1151  		return err
  1152  	}
  1153  	c.InboxLayoutCb <- uilayout
  1154  	return nil
  1155  }
  1156  
  1157  func (c *ChatUI) ChatInboxFailed(ctx context.Context, arg chat1.ChatInboxFailedArg) error {
  1158  	c.InboxCb <- NonblockInboxResult{
  1159  		Err: fmt.Errorf("%s", arg.Error.Message),
  1160  	}
  1161  	return nil
  1162  }
  1163  
  1164  func (c *ChatUI) ChatInboxUnverified(ctx context.Context, arg chat1.ChatInboxUnverifiedArg) error {
  1165  	var inbox chat1.UnverifiedInboxUIItems
  1166  	if err := json.Unmarshal([]byte(arg.Inbox), &inbox); err != nil {
  1167  		return err
  1168  	}
  1169  	c.InboxCb <- NonblockInboxResult{
  1170  		InboxRes: &inbox,
  1171  	}
  1172  	return nil
  1173  }
  1174  
  1175  func (c *ChatUI) ChatThreadCached(ctx context.Context, arg *string) error {
  1176  	var thread chat1.UIMessages
  1177  	if arg == nil {
  1178  		c.ThreadCb <- NonblockThreadResult{
  1179  			Thread: nil,
  1180  			Full:   false,
  1181  		}
  1182  	} else {
  1183  		if err := json.Unmarshal([]byte(*arg), &thread); err != nil {
  1184  			return err
  1185  		}
  1186  		c.ThreadCb <- NonblockThreadResult{
  1187  			Thread: &thread,
  1188  			Full:   false,
  1189  		}
  1190  	}
  1191  	return nil
  1192  }
  1193  
  1194  func (c *ChatUI) ChatThreadFull(ctx context.Context, arg string) error {
  1195  	var thread chat1.UIMessages
  1196  	if err := json.Unmarshal([]byte(arg), &thread); err != nil {
  1197  		return err
  1198  	}
  1199  	c.ThreadCb <- NonblockThreadResult{
  1200  		Thread: &thread,
  1201  		Full:   true,
  1202  	}
  1203  	return nil
  1204  }
  1205  
  1206  func (c *ChatUI) ChatThreadStatus(ctx context.Context, status chat1.UIChatThreadStatus) error {
  1207  	c.ThreadStatusCb <- status
  1208  	return nil
  1209  }
  1210  
  1211  func (c *ChatUI) ChatConfirmChannelDelete(ctx context.Context, arg chat1.ChatConfirmChannelDeleteArg) (bool, error) {
  1212  	return true, nil
  1213  }
  1214  
  1215  func (c *ChatUI) ChatSearchHit(ctx context.Context, arg chat1.ChatSearchHitArg) error {
  1216  	c.SearchHitCb <- arg
  1217  	return nil
  1218  }
  1219  
  1220  func (c *ChatUI) ChatSearchDone(ctx context.Context, arg chat1.ChatSearchDoneArg) error {
  1221  	c.SearchDoneCb <- arg
  1222  	return nil
  1223  }
  1224  
  1225  func (c *ChatUI) ChatSearchInboxHit(ctx context.Context, arg chat1.ChatSearchInboxHitArg) error {
  1226  	c.InboxSearchHitCb <- arg
  1227  	return nil
  1228  }
  1229  
  1230  func (c *ChatUI) ChatSearchConvHits(ctx context.Context, hits chat1.UIChatSearchConvHits) error {
  1231  	c.InboxSearchConvHitsCb <- hits.Hits
  1232  	return nil
  1233  }
  1234  
  1235  func (c *ChatUI) ChatSearchTeamHits(ctx context.Context, hits chat1.UIChatSearchTeamHits) error {
  1236  	return nil
  1237  }
  1238  
  1239  func (c *ChatUI) ChatSearchBotHits(ctx context.Context, hits chat1.UIChatSearchBotHits) error {
  1240  	return nil
  1241  }
  1242  
  1243  func (c *ChatUI) ChatSearchInboxStart(ctx context.Context) error {
  1244  	return nil
  1245  }
  1246  
  1247  func (c *ChatUI) ChatSearchInboxDone(ctx context.Context, arg chat1.ChatSearchInboxDoneArg) error {
  1248  	c.InboxSearchDoneCb <- arg
  1249  	return nil
  1250  }
  1251  
  1252  func (c *ChatUI) ChatSearchIndexStatus(ctx context.Context, arg chat1.ChatSearchIndexStatusArg) error {
  1253  	return nil
  1254  }
  1255  
  1256  func (c *ChatUI) ChatStellarShowConfirm(ctx context.Context) error {
  1257  	c.StellarShowConfirm <- struct{}{}
  1258  	return nil
  1259  }
  1260  
  1261  func (c *ChatUI) ChatStellarDataConfirm(ctx context.Context, summary chat1.UIChatPaymentSummary) (bool, error) {
  1262  	c.StellarDataConfirm <- summary
  1263  	return true, nil
  1264  }
  1265  
  1266  func (c *ChatUI) ChatStellarDataError(ctx context.Context, err keybase1.Status) (bool, error) {
  1267  	c.StellarDataError <- err
  1268  	return false, nil
  1269  }
  1270  
  1271  func (c *ChatUI) ChatStellarDone(ctx context.Context, canceled bool) error {
  1272  	c.StellarDone <- struct{}{}
  1273  	return nil
  1274  }
  1275  
  1276  func (c *ChatUI) ChatShowManageChannels(ctx context.Context, teamname string) error {
  1277  	c.ShowManageChannels <- teamname
  1278  	return nil
  1279  }
  1280  
  1281  func (c *ChatUI) ChatGiphySearchResults(ctx context.Context, convID chat1.ConversationID,
  1282  	results chat1.GiphySearchResults) error {
  1283  	c.GiphyResults <- results
  1284  	return nil
  1285  }
  1286  
  1287  func (c *ChatUI) ChatGiphyToggleResultWindow(ctx context.Context,
  1288  	convID chat1.ConversationID, show, clearInput bool) error {
  1289  	c.GiphyWindow <- show
  1290  	return nil
  1291  }
  1292  
  1293  func (c *ChatUI) ChatCoinFlipStatus(ctx context.Context, updates []chat1.UICoinFlipStatus) error {
  1294  	c.CoinFlipUpdates <- updates
  1295  	return nil
  1296  }
  1297  
  1298  func (c *ChatUI) ChatCommandMarkdown(ctx context.Context, convID chat1.ConversationID,
  1299  	md *chat1.UICommandMarkdown) error {
  1300  	c.CommandMarkdown <- md
  1301  	return nil
  1302  }
  1303  
  1304  func (c *ChatUI) ChatMaybeMentionUpdate(ctx context.Context, teamName, channel string,
  1305  	info chat1.UIMaybeMentionInfo) error {
  1306  	return nil
  1307  }
  1308  
  1309  func (c *ChatUI) ChatLoadGalleryHit(ctx context.Context, msg chat1.UIMessage) error {
  1310  	return nil
  1311  }
  1312  
  1313  func (c *ChatUI) ChatWatchPosition(context.Context, chat1.ConversationID, chat1.UIWatchPositionPerm) (chat1.LocationWatchID, error) {
  1314  	return chat1.LocationWatchID(0), nil
  1315  }
  1316  
  1317  func (c *ChatUI) ChatClearWatch(context.Context, chat1.LocationWatchID) error {
  1318  	return nil
  1319  }
  1320  
  1321  func (c *ChatUI) ChatCommandStatus(context.Context, chat1.ConversationID, string,
  1322  	chat1.UICommandStatusDisplayTyp, []chat1.UICommandStatusActionTyp) error {
  1323  	return nil
  1324  }
  1325  
  1326  func (c *ChatUI) ChatBotCommandsUpdateStatus(context.Context, chat1.ConversationID,
  1327  	chat1.UIBotCommandsUpdateStatus) error {
  1328  	return nil
  1329  }
  1330  
  1331  func (c *ChatUI) TriggerContactSync(context.Context) error {
  1332  	return nil
  1333  }
  1334  
  1335  type DummyAssetDeleter struct{}
  1336  
  1337  func NewDummyAssetDeleter() DummyAssetDeleter {
  1338  	return DummyAssetDeleter{}
  1339  }
  1340  
  1341  // DeleteAssets implements github.com/keybase/go/chat/storage/storage.AssetDeleter interface.
  1342  func (d DummyAssetDeleter) DeleteAssets(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID, assets []chat1.Asset) {
  1343  }
  1344  
  1345  func MockSentMessages(g *libkb.GlobalContext, t libkb.TestingTB) []MockMessage {
  1346  	if g.ChatHelper == nil {
  1347  		t.Fatal("ChatHelper is nil")
  1348  	}
  1349  	mch, ok := g.ChatHelper.(*MockChatHelper)
  1350  	if !ok {
  1351  		t.Fatalf("ChatHelper isn't a mock: %T", g.ChatHelper)
  1352  	}
  1353  	return mch.SentMessages
  1354  }
  1355  
  1356  // MockMessage only supports what we're currently testing (system message for git push).
  1357  type MockMessage struct {
  1358  	name        string
  1359  	topicName   *string
  1360  	membersType chat1.ConversationMembersType
  1361  	ident       keybase1.TLFIdentifyBehavior
  1362  	Body        chat1.MessageBody
  1363  	MsgType     chat1.MessageType
  1364  }
  1365  
  1366  type MockChatHelper struct {
  1367  	SentMessages []MockMessage
  1368  	convs        map[string]chat1.ConversationLocal
  1369  }
  1370  
  1371  var _ libkb.ChatHelper = (*MockChatHelper)(nil)
  1372  
  1373  func NewMockChatHelper() *MockChatHelper {
  1374  	return &MockChatHelper{
  1375  		convs: make(map[string]chat1.ConversationLocal),
  1376  	}
  1377  }
  1378  
  1379  func (m *MockChatHelper) SendTextByID(ctx context.Context, convID chat1.ConversationID,
  1380  	tlfName string, text string, vis keybase1.TLFVisibility) error {
  1381  	return nil
  1382  }
  1383  func (m *MockChatHelper) SendMsgByID(ctx context.Context, convID chat1.ConversationID,
  1384  	tlfName string, body chat1.MessageBody, msgType chat1.MessageType, vis keybase1.TLFVisibility) error {
  1385  	return nil
  1386  }
  1387  func (m *MockChatHelper) SendTextByIDNonblock(ctx context.Context, convID chat1.ConversationID,
  1388  	tlfName string, text string, outboxID *chat1.OutboxID, replyTo *chat1.MessageID) (chat1.OutboxID, error) {
  1389  	return chat1.OutboxID{}, nil
  1390  }
  1391  func (m *MockChatHelper) SendMsgByIDNonblock(ctx context.Context, convID chat1.ConversationID,
  1392  	tlfName string, body chat1.MessageBody, msgType chat1.MessageType, inOutboxID *chat1.OutboxID,
  1393  	replyTo *chat1.MessageID) (chat1.OutboxID, error) {
  1394  	return chat1.OutboxID{}, nil
  1395  }
  1396  func (m *MockChatHelper) SendTextByName(ctx context.Context, name string, topicName *string,
  1397  	membersType chat1.ConversationMembersType, ident keybase1.TLFIdentifyBehavior, text string) error {
  1398  	rb, err := libkb.RandBytes(10)
  1399  	if err != nil {
  1400  		return err
  1401  	}
  1402  	// use this to fake making channels...
  1403  	_, ok := m.convs[m.convKey(name, topicName)]
  1404  	if !ok {
  1405  		m.convs[m.convKey(name, topicName)] = chat1.ConversationLocal{
  1406  			Info: chat1.ConversationInfoLocal{
  1407  				Id:        rb,
  1408  				TopicName: *topicName,
  1409  			},
  1410  		}
  1411  	}
  1412  
  1413  	return nil
  1414  }
  1415  func (m *MockChatHelper) SendMsgByName(ctx context.Context, name string, topicName *string,
  1416  	membersType chat1.ConversationMembersType, ident keybase1.TLFIdentifyBehavior, body chat1.MessageBody,
  1417  	msgType chat1.MessageType) error {
  1418  	m.SentMessages = append(m.SentMessages, MockMessage{
  1419  		name:        name,
  1420  		topicName:   topicName,
  1421  		membersType: membersType,
  1422  		ident:       ident,
  1423  		Body:        body,
  1424  		MsgType:     msgType,
  1425  	})
  1426  	return nil
  1427  }
  1428  func (m *MockChatHelper) SendTextByNameNonblock(ctx context.Context, name string, topicName *string,
  1429  	membersType chat1.ConversationMembersType, ident keybase1.TLFIdentifyBehavior, text string, outboxID *chat1.OutboxID) (chat1.OutboxID, error) {
  1430  	return chat1.OutboxID{}, nil
  1431  }
  1432  func (m *MockChatHelper) SendMsgByNameNonblock(ctx context.Context, name string, topicName *string,
  1433  	membersType chat1.ConversationMembersType, ident keybase1.TLFIdentifyBehavior, body chat1.MessageBody,
  1434  	msgType chat1.MessageType, outboxID *chat1.OutboxID) (chat1.OutboxID, error) {
  1435  	m.SentMessages = append(m.SentMessages, MockMessage{
  1436  		name:        name,
  1437  		topicName:   topicName,
  1438  		membersType: membersType,
  1439  		ident:       ident,
  1440  		Body:        body,
  1441  		MsgType:     msgType,
  1442  	})
  1443  	return chat1.OutboxID{}, nil
  1444  }
  1445  
  1446  func (m *MockChatHelper) DeleteMsg(ctx context.Context, convID chat1.ConversationID, tlfName string,
  1447  	msgID chat1.MessageID) error {
  1448  	return nil
  1449  }
  1450  
  1451  func (m *MockChatHelper) DeleteMsgNonblock(ctx context.Context, convID chat1.ConversationID, tlfName string,
  1452  	msgID chat1.MessageID) error {
  1453  	return nil
  1454  }
  1455  
  1456  func (m *MockChatHelper) FindConversations(ctx context.Context, name string,
  1457  	topicName *string, topicType chat1.TopicType,
  1458  	membersType chat1.ConversationMembersType, vis keybase1.TLFVisibility) ([]chat1.ConversationLocal, error) {
  1459  
  1460  	conv, ok := m.convs[m.convKey(name, topicName)]
  1461  	if ok {
  1462  		return []chat1.ConversationLocal{conv}, nil
  1463  	}
  1464  
  1465  	return nil, nil
  1466  }
  1467  
  1468  func (m *MockChatHelper) FindConversationsByID(ctx context.Context, convIDs []chat1.ConversationID) (convs []chat1.ConversationLocal, err error) {
  1469  	for _, id := range convIDs {
  1470  		for _, v := range m.convs {
  1471  			if bytes.Equal(v.Info.Id, id) {
  1472  				convs = append(convs, v)
  1473  			}
  1474  		}
  1475  	}
  1476  	return convs, nil
  1477  }
  1478  
  1479  func (m *MockChatHelper) GetChannelTopicName(ctx context.Context, teamID keybase1.TeamID,
  1480  	topicType chat1.TopicType, convID chat1.ConversationID) (string, error) {
  1481  	for _, v := range m.convs {
  1482  		if v.Info.Id.Eq(convID) {
  1483  			return v.Info.TopicName, nil
  1484  		}
  1485  	}
  1486  	return "", fmt.Errorf("MockChatHelper.GetChannelTopicName conv not found %v", convID)
  1487  }
  1488  
  1489  func (m *MockChatHelper) UpgradeKBFSToImpteam(ctx context.Context, tlfName string, tlfID chat1.TLFID,
  1490  	public bool) error {
  1491  	return nil
  1492  }
  1493  
  1494  func (m *MockChatHelper) GetMessages(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID,
  1495  	msgIDs []chat1.MessageID, resolveSupersedes bool, reason *chat1.GetThreadReason) ([]chat1.MessageUnboxed, error) {
  1496  	return nil, nil
  1497  }
  1498  
  1499  func (m *MockChatHelper) GetMessage(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID,
  1500  	msgID chat1.MessageID, resolveSupersedes bool, reason *chat1.GetThreadReason) (chat1.MessageUnboxed, error) {
  1501  	return chat1.MessageUnboxed{}, nil
  1502  }
  1503  
  1504  func (m *MockChatHelper) UserReacjis(ctx context.Context, uid gregor1.UID) keybase1.UserReacjis {
  1505  	return keybase1.UserReacjis{}
  1506  }
  1507  
  1508  func (m *MockChatHelper) JourneycardTimeTravel(ctx context.Context, uid gregor1.UID, duration time.Duration) (int, int, error) {
  1509  	return 0, 0, fmt.Errorf("JourneycardTimeTravel not implemented on mock")
  1510  }
  1511  
  1512  func (m *MockChatHelper) JourneycardResetAllConvs(ctx context.Context, uid gregor1.UID) error {
  1513  	return fmt.Errorf("JourneycardResetAllConvs not implemented on mock")
  1514  }
  1515  
  1516  func (m *MockChatHelper) JourneycardDebugState(ctx context.Context, uid gregor1.UID, teamID keybase1.TeamID) (string, error) {
  1517  	return "", fmt.Errorf("JourneycardDebugState not implemented on mock")
  1518  }
  1519  
  1520  func (m *MockChatHelper) NewConversation(ctx context.Context, uid gregor1.UID, tlfName string,
  1521  	topicName *string, topicType chat1.TopicType, membersType chat1.ConversationMembersType,
  1522  	vis keybase1.TLFVisibility) (chat1.ConversationLocal, bool, error) {
  1523  	return chat1.ConversationLocal{}, false, nil
  1524  }
  1525  
  1526  func (m *MockChatHelper) NewConversationSkipFindExisting(ctx context.Context, uid gregor1.UID, tlfName string,
  1527  	topicName *string, topicType chat1.TopicType, membersType chat1.ConversationMembersType,
  1528  	vis keybase1.TLFVisibility) (chat1.ConversationLocal, bool, error) {
  1529  	return chat1.ConversationLocal{}, false, nil
  1530  }
  1531  
  1532  func (m *MockChatHelper) NewConversationWithMemberSourceConv(ctx context.Context, uid gregor1.UID, tlfName string,
  1533  	topicName *string, topicType chat1.TopicType, membersType chat1.ConversationMembersType,
  1534  	vis keybase1.TLFVisibility, retentionPolicy *chat1.RetentionPolicy,
  1535  	memberSourceConv *chat1.ConversationID) (chat1.ConversationLocal, bool, error) {
  1536  	return chat1.ConversationLocal{}, false, nil
  1537  }
  1538  
  1539  func (m *MockChatHelper) JoinConversationByID(ctx context.Context, uid gregor1.UID,
  1540  	convID chat1.ConversationID) error {
  1541  	return nil
  1542  }
  1543  
  1544  func (m *MockChatHelper) JoinConversationByName(ctx context.Context, uid gregor1.UID, tlfName,
  1545  	topicName string, topicType chat1.TopicType, vid keybase1.TLFVisibility) error {
  1546  	return nil
  1547  }
  1548  
  1549  func (m *MockChatHelper) LeaveConversation(ctx context.Context, uid gregor1.UID, convID chat1.ConversationID) error {
  1550  	return nil
  1551  }
  1552  
  1553  func (m *MockChatHelper) InTeam(context.Context, gregor1.UID, keybase1.TeamID) (bool, error) {
  1554  	return false, nil
  1555  }
  1556  
  1557  func (m *MockChatHelper) convKey(name string, topicName *string) string {
  1558  	if topicName == nil {
  1559  		return name + ":general"
  1560  	}
  1561  	return name + ":" + *topicName
  1562  }
  1563  
  1564  func (m *MockChatHelper) BulkAddToConv(context.Context, gregor1.UID, chat1.ConversationID, []string) error {
  1565  	return nil
  1566  }
  1567  
  1568  type MockUIRouter struct {
  1569  	ui libkb.ChatUI
  1570  }
  1571  
  1572  var _ libkb.UIRouter = (*MockUIRouter)(nil)
  1573  
  1574  func NewMockUIRouter(chatUI libkb.ChatUI) *MockUIRouter {
  1575  	return &MockUIRouter{
  1576  		ui: chatUI,
  1577  	}
  1578  }
  1579  
  1580  func (f *MockUIRouter) SetUI(libkb.ConnectionID, libkb.UIKind)   {}
  1581  func (f *MockUIRouter) GetIdentifyUI() (libkb.IdentifyUI, error) { return nil, nil }
  1582  func (f *MockUIRouter) GetIdentifyUICtx(ctx context.Context) (int, libkb.IdentifyUI, error) {
  1583  	return 0, nil, nil
  1584  }
  1585  func (f *MockUIRouter) GetSecretUI(sessionID int) (libkb.SecretUI, error)         { return nil, nil }
  1586  func (f *MockUIRouter) GetRekeyUI() (keybase1.RekeyUIInterface, int, error)       { return nil, 0, nil }
  1587  func (f *MockUIRouter) GetRekeyUINoSessionID() (keybase1.RekeyUIInterface, error) { return nil, nil }
  1588  func (f *MockUIRouter) GetHomeUI() (keybase1.HomeUIInterface, error)              { return nil, nil }
  1589  func (f *MockUIRouter) GetIdentify3UIAdapter(libkb.MetaContext) (libkb.IdentifyUI, error) {
  1590  	return nil, nil
  1591  }
  1592  func (f *MockUIRouter) GetIdentify3UI(libkb.MetaContext) (keybase1.Identify3UiInterface, error) {
  1593  	return nil, nil
  1594  }
  1595  func (f *MockUIRouter) GetLogUI() (libkb.LogUI, error)                                { return nil, nil }
  1596  func (f *MockUIRouter) WaitForUIType(uiKind libkb.UIKind, timeout time.Duration) bool { return false }
  1597  func (f *MockUIRouter) DumpUIs() map[libkb.UIKind]libkb.ConnectionID                  { return nil }
  1598  func (f *MockUIRouter) Shutdown()                                                     {}
  1599  
  1600  func (f *MockUIRouter) GetChatUI() (libkb.ChatUI, error) {
  1601  	return f.ui, nil
  1602  }