1package service
2
3import (
4	"crypto/rand"
5	"errors"
6	"fmt"
7	"testing"
8	"time"
9
10	"golang.org/x/net/context"
11
12	"github.com/keybase/client/go/badges"
13	"github.com/keybase/client/go/chat"
14	"github.com/keybase/client/go/chat/globals"
15	"github.com/keybase/client/go/gregor"
16	grclient "github.com/keybase/client/go/gregor/client"
17	"github.com/keybase/client/go/gregor/storage"
18	grutils "github.com/keybase/client/go/gregor/utils"
19	"github.com/keybase/client/go/kbtest"
20	"github.com/keybase/client/go/libkb"
21	"github.com/keybase/client/go/logger"
22	"github.com/keybase/client/go/protocol/chat1"
23	"github.com/keybase/client/go/protocol/gregor1"
24	"github.com/keybase/client/go/protocol/keybase1"
25	"github.com/keybase/clockwork"
26	"github.com/stretchr/testify/require"
27)
28
29func broadcastMessageTesting(t *testing.T, h *gregorHandler, m gregor1.Message) error {
30	require.NoError(t, h.BroadcastMessage(context.TODO(), m))
31	select {
32	case err := <-h.testingEvents.broadcastSentCh:
33		return err
34	case <-time.After(20 * time.Second):
35		require.Fail(t, "broadcast didn't complete")
36	}
37	return nil
38}
39
40func setupGregorTest(t *testing.T) (libkb.TestContext, *globals.Context) {
41	tc := libkb.SetupTest(t, "gregor", 2)
42	g := globals.NewContext(tc.G, &globals.ChatContext{})
43	g.CtxFactory = chat.NewCtxFactory(g)
44	return tc, g
45}
46
47func TestGregorHandler(t *testing.T) {
48	tc, g := setupGregorTest(t)
49	defer tc.Cleanup()
50
51	tc.G.SetService()
52
53	listener := newNlistener(t)
54	tc.G.NotifyRouter.AddListener(listener)
55
56	user, err := kbtest.CreateAndSignupFakeUser("gregr", tc.G)
57	require.NoError(t, err)
58
59	h := newGregorHandler(g)
60	h.Init()
61	h.testingEvents = newTestingEvents()
62	_, err = h.resetGregorClient(context.TODO(), gregor1.UID(user.User.GetUID().ToBytes()), gregor1.DeviceID{})
63	require.NoError(t, err)
64	require.Equal(t, "gregor", h.HandlerName(), "wrong name")
65
66	kbUID := user.User.GetUID()
67	gUID := gregor1.UID(kbUID.ToBytes())
68
69	h.PushHandler(newKBFSFavoritesHandler(tc.G))
70
71	m := gregor1.Message{
72		Ibm_: &gregor1.InBandMessage{
73			StateUpdate_: &gregor1.StateUpdateMessage{
74				Md_: gregor1.Metadata{
75					Uid_:   gUID,
76					MsgID_: newMsgID(),
77				},
78				Creation_: &gregor1.Item{
79					Category_: "kbfs.favorites",
80					Body_:     gregor1.Body(`{"action": "delete", "tlf":"/private/t_alice,t_bob"}`),
81				},
82			},
83		},
84	}
85
86	err = broadcastMessageTesting(t, h, m)
87	require.NoError(t, err)
88	require.Equal(t, 1, len(listener.favoritesChanged), "num faves failure")
89	require.Equal(t, kbUID, listener.favoritesChanged[0], "wrong uid")
90}
91
92type nlistener struct {
93	libkb.NoopNotifyListener
94	t                *testing.T
95	favoritesChanged []keybase1.UID
96	badgeState       chan keybase1.BadgeState
97	threadStale      chan []chat1.ConversationStaleUpdate
98	testChanTimeout  time.Duration
99}
100
101var _ libkb.NotifyListener = (*nlistener)(nil)
102
103func newNlistener(t *testing.T) *nlistener {
104	return &nlistener{
105		t:               t,
106		badgeState:      make(chan keybase1.BadgeState, 1),
107		threadStale:     make(chan []chat1.ConversationStaleUpdate, 1),
108		testChanTimeout: 20 * time.Second,
109	}
110}
111
112func (n *nlistener) FavoritesChanged(uid keybase1.UID) {
113	n.favoritesChanged = append(n.favoritesChanged, uid)
114}
115func (n *nlistener) ChatThreadsStale(uid keybase1.UID, cids []chat1.ConversationStaleUpdate) {
116	select {
117	case n.threadStale <- cids:
118	case <-time.After(n.testChanTimeout):
119		require.Fail(n.t, "thread send timeout")
120	}
121}
122func (n *nlistener) BadgeState(badgeState keybase1.BadgeState) {
123	select {
124	case n.badgeState <- badgeState:
125	case <-time.After(n.testChanTimeout):
126		require.Fail(n.t, "badgestate not read")
127	}
128}
129
130func (n *nlistener) getBadgeState(t *testing.T) keybase1.BadgeState {
131	select {
132	case x := <-n.badgeState:
133		return x
134	case <-time.After(n.testChanTimeout):
135		require.Fail(t, "badgestate not received")
136		return keybase1.BadgeState{}
137	}
138}
139
140type showTrackerPopupIdentifyUI struct {
141	kbtest.FakeIdentifyUI
142	startCh   chan string
143	dismissCh chan string
144}
145
146func newShowTrackerPopupIdentifyUI() *showTrackerPopupIdentifyUI {
147	return &showTrackerPopupIdentifyUI{
148		startCh:   make(chan string, 1),
149		dismissCh: make(chan string, 1),
150	}
151}
152
153var _ libkb.IdentifyUI = (*showTrackerPopupIdentifyUI)(nil)
154
155func (ui *showTrackerPopupIdentifyUI) Start(_ libkb.MetaContext, name string, reason keybase1.IdentifyReason, force bool) error {
156	ui.startCh <- name
157	return nil
158}
159
160// Overriding the Dismiss method lets us test that it gets called.
161func (ui *showTrackerPopupIdentifyUI) Dismiss(_ libkb.MetaContext, username string, _ keybase1.DismissReason) error {
162	ui.dismissCh <- username
163	return nil
164}
165
166// Test that when we inject a gregor "show_tracker_popup" message containing a
167// given UID into a gregorHandler, the result is that a TrackEngine gets run
168// for that user.
169func TestShowTrackerPopupMessage(t *testing.T) {
170	tc, g := setupGregorTest(t)
171	defer tc.Cleanup()
172
173	tc.G.SetService()
174
175	identifyUI := newShowTrackerPopupIdentifyUI()
176	router := fakeUIRouter{
177		secretUI:   &libkb.TestSecretUI{},
178		identifyUI: identifyUI,
179	}
180	tc.G.SetUIRouter(&router)
181
182	idhandler := NewIdentifyUIHandler(tc.G, 0)
183	idhandler.toggleAlwaysAlive(true)
184
185	trackee, err := kbtest.CreateAndSignupFakeUser("gregr", tc.G)
186	require.NoError(t, err)
187
188	// Create another test user to actually perform the track, because we can't track ourselves.
189	tracker, err := kbtest.CreateAndSignupFakeUser("gregr", tc.G)
190	require.NoError(t, err)
191
192	h := newGregorHandler(g)
193	h.Init()
194	h.testingEvents = newTestingEvents()
195	_, err = h.resetGregorClient(context.TODO(), gregor1.UID(tracker.User.GetUID().ToBytes()), gregor1.DeviceID{})
196	require.NoError(t, err)
197
198	h.PushHandler(idhandler)
199
200	msgID := gregor1.MsgID("my_random_id")
201	m := gregor1.Message{
202		Ibm_: &gregor1.InBandMessage{
203			StateUpdate_: &gregor1.StateUpdateMessage{
204				Md_: gregor1.Metadata{
205					MsgID_: msgID,
206					Uid_:   gregor1.UID(tracker.User.GetUID().ToBytes()),
207				},
208				Creation_: &gregor1.Item{
209					Category_: gregor1.Category("show_tracker_popup"),
210					Body_:     gregor1.Body(fmt.Sprintf(`{"uid": "%s"}`, trackee.User.GetUID())),
211				},
212			},
213		},
214	}
215
216	err = broadcastMessageTesting(t, h, m)
217	require.NoError(t, err)
218	select {
219	case name := <-identifyUI.startCh:
220		require.Equal(t, trackee.Username, name, "wrong username")
221	case <-time.After(20 * time.Second):
222		require.Fail(t, "no start username")
223	}
224	select {
225	case <-identifyUI.dismissCh:
226		require.Fail(t, "no dismiss should have happened")
227	default:
228	}
229
230	msgIDDis := gregor1.MsgID("my_random_id_dis")
231	dismissal := gregor1.Message{
232		Ibm_: &gregor1.InBandMessage{
233			StateUpdate_: &gregor1.StateUpdateMessage{
234				Md_: gregor1.Metadata{
235					MsgID_: msgIDDis,
236					Uid_:   gregor1.UID(tracker.User.GetUID().ToBytes()),
237				},
238				Dismissal_: &gregor1.Dismissal{
239					MsgIDs_: []gregor1.MsgID{msgID},
240				},
241			},
242		},
243	}
244	err = broadcastMessageTesting(t, h, dismissal)
245	require.NoError(t, err)
246	select {
247	case name := <-identifyUI.dismissCh:
248		require.Equal(t, trackee.User.GetName(), name, "wrong dismiss")
249	case <-time.After(20 * time.Second):
250		require.Fail(t, "no dismiss username")
251	}
252}
253
254func newMsgID() gregor1.MsgID {
255	ret := make([]byte, 16)
256	_, _ = rand.Read(ret)
257	return ret
258}
259
260type mockGregord struct {
261	sm  gregor.StateMachine
262	fc  clockwork.FakeClock
263	log logger.Logger
264}
265
266func (m mockGregord) SyncAll(ctx context.Context, arg chat1.SyncAllArg) (res chat1.SyncAllResult, err error) {
267	sres, err := m.Sync(ctx, gregor1.SyncArg{
268		Uid:      arg.Uid,
269		Deviceid: arg.DeviceID,
270		Ctime:    arg.Ctime,
271	})
272	if err != nil {
273		return res, err
274	}
275
276	res.Notification = chat1.NewSyncAllNotificationResWithIncremental(sres)
277	return res, nil
278}
279
280func (m mockGregord) Sync(ctx context.Context, arg gregor1.SyncArg) (gregor1.SyncResult, error) {
281	var res gregor1.SyncResult
282	msgs, err := m.sm.InBandMessagesSince(ctx, arg.UID(), arg.DeviceID(), arg.CTime())
283	if err != nil {
284		return res, err
285	}
286	state, err := m.sm.State(ctx, arg.UID(), arg.DeviceID(), nil)
287	if err != nil {
288		return res, err
289	}
290	hash, err := state.Hash()
291	if err != nil {
292		return res, err
293	}
294	for _, msg := range msgs {
295		if msg, ok := msg.(gregor1.InBandMessage); ok {
296			res.Msgs = append(res.Msgs, msg)
297		} else {
298			m.log.Warning("Bad cast in serveSync (type=%T): %+v", msg)
299		}
300	}
301	res.Hash = hash
302	return res, nil
303}
304
305func (m mockGregord) ConsumeMessage(ctx context.Context, msg gregor1.Message) error {
306	m.log.Debug("mockGregord: ConsumeMessage: msgID: %s Ctime: %s", msg.ToInBandMessage().Metadata().MsgID(),
307		msg.ToInBandMessage().Metadata().CTime())
308	_, err := m.sm.ConsumeMessage(ctx, msg)
309	return err
310}
311
312func (m mockGregord) ConsumeMessageMulti(ctx context.Context, arg gregor1.ConsumeMessageMultiArg) error {
313	return errors.New("unimplemented")
314}
315
316func (m mockGregord) ConsumePublishMessage(_ context.Context, _ gregor1.Message) error {
317	return errors.New("unimplemented")
318}
319func (m mockGregord) Ping(_ context.Context) (string, error) {
320	return "", nil
321}
322func (m mockGregord) State(ctx context.Context, arg gregor1.StateArg) (gregor1.State, error) {
323	state, err := m.sm.State(ctx, arg.Uid, arg.Deviceid, arg.TimeOrOffset)
324	if err != nil {
325		return gregor1.State{}, err
326	}
327	return state.(gregor1.State), nil
328}
329func (m mockGregord) StateByCategoryPrefix(_ context.Context, _ gregor1.StateByCategoryPrefixArg) (gregor1.State, error) {
330	return gregor1.State{}, errors.New("unimplemented")
331}
332func (m mockGregord) Version(_ context.Context, _ gregor1.UID) (string, error) {
333	return "mock", nil
334}
335func (m mockGregord) DescribeConnectedUsers(ctx context.Context, arg []gregor1.UID) ([]gregor1.ConnectedUser, error) {
336	return nil, nil
337}
338func (m mockGregord) DescribeConnectedUsersInternal(ctx context.Context, arg []gregor1.UID) ([]gregor1.ConnectedUser, error) {
339	return nil, nil
340}
341
342func (m mockGregord) newIbm(uid gregor1.UID) gregor1.Message {
343	m.fc.Advance(time.Minute)
344	return gregor1.Message{
345		Ibm_: &gregor1.InBandMessage{
346			StateUpdate_: &gregor1.StateUpdateMessage{
347				Md_: gregor1.Metadata{
348					Uid_:   uid,
349					MsgID_: newMsgID(),
350					Ctime_: gregor1.ToTime(m.fc.Now()),
351				},
352				Creation_: &gregor1.Item{
353					Category_: "unknown!",
354					Body_:     gregor1.Body([]byte("HIHIHI")),
355				},
356			},
357		},
358	}
359}
360
361func (m mockGregord) newIbm2(uid gregor1.UID, category gregor1.Category, body gregor1.Body) gregor1.Message {
362	m.fc.Advance(time.Minute)
363	return gregor1.Message{
364		Ibm_: &gregor1.InBandMessage{
365			StateUpdate_: &gregor1.StateUpdateMessage{
366				Md_: gregor1.Metadata{
367					Uid_:   uid,
368					MsgID_: newMsgID(),
369					Ctime_: gregor1.ToTime(m.fc.Now()),
370				},
371				Creation_: &gregor1.Item{
372					Category_: category,
373					Body_:     body,
374				},
375			},
376		},
377	}
378}
379
380func (m mockGregord) newDismissal(uid gregor1.UID, msg gregor.Message) gregor.Message {
381	m.fc.Advance(time.Minute)
382	dismissalID := msg.ToInBandMessage().Metadata().MsgID().(gregor1.MsgID)
383	return gregor1.Message{
384		Ibm_: &gregor1.InBandMessage{
385			StateUpdate_: &gregor1.StateUpdateMessage{
386				Md_: gregor1.Metadata{
387					Uid_:   uid,
388					MsgID_: newMsgID(),
389					Ctime_: gregor1.ToTime(m.fc.Now()),
390				},
391				Dismissal_: &gregor1.Dismissal{
392					MsgIDs_: []gregor1.MsgID{dismissalID},
393				},
394			},
395		},
396	}
397}
398
399func newGregordMock(logger logger.Logger) mockGregord {
400	var of gregor1.ObjFactory
401	fc := clockwork.NewFakeClock()
402
403	sm := storage.NewMemEngine(of, fc, logger)
404
405	return mockGregord{sm: sm, fc: fc, log: logger}
406}
407
408func setupSyncTests(t *testing.T, g *globals.Context) (*gregorHandler, mockGregord, gregor1.UID) {
409	user, err := kbtest.CreateAndSignupFakeUser("gregr", g.ExternalG())
410	require.NoError(t, err)
411	uid := gregor1.UID(user.User.GetUID().ToBytes())
412	deviceID := gregor1.DeviceID{}
413
414	h := newGregorHandler(g)
415	h.Init()
416	h.testingEvents = newTestingEvents()
417	_, err = h.resetGregorClient(context.TODO(), uid, deviceID)
418	require.NoError(t, err)
419
420	server := newGregordMock(g.ExternalG().Log)
421
422	return h, server, uid
423}
424
425func checkMessages(t *testing.T, source string, msgs []gregor.InBandMessage,
426	refMsgs []gregor.InBandMessage) {
427	require.Len(t, msgs, len(refMsgs))
428	for index, refMsg := range refMsgs {
429		msg := msgs[index]
430		msgID := msg.Metadata().MsgID()
431		refMsgID := refMsg.Metadata().MsgID()
432		require.Equal(t, refMsgID.Bytes(), msgID.Bytes())
433	}
434}
435
436func doServerSync(t *testing.T, h *gregorHandler, srv mockGregord) ([]gregor.InBandMessage, []gregor.InBandMessage) {
437	_, token, _, _, _ := h.loggedIn(context.TODO())
438	pctime := h.gregorCli.StateMachineLatestCTime(context.TODO())
439	ctime := gregor1.Time(0)
440	if pctime != nil {
441		ctime = gregor1.ToTime(*pctime)
442	}
443	sres, err := srv.SyncAll(context.TODO(), chat1.SyncAllArg{
444		Uid:      h.gregorCli.User.(gregor1.UID),
445		DeviceID: h.gregorCli.Device.(gregor1.DeviceID),
446		Session:  gregor1.SessionToken(token),
447		Ctime:    ctime,
448	})
449	require.NoError(t, err)
450	c, err := h.serverSync(context.TODO(), srv, h.gregorCli, &sres.Notification)
451	require.NoError(t, err)
452	require.NotNil(t, h.testingEvents)
453	select {
454	case r := <-h.testingEvents.replayThreadCh:
455		require.NoError(t, r.err)
456		return r.replayed, c
457	case <-time.After(20 * time.Second):
458		require.Fail(t, "no replay event received")
459		return nil, nil
460	}
461}
462
463func TestSyncFresh(t *testing.T) {
464	tc, g := setupGregorTest(t)
465	defer tc.Cleanup()
466	tc.G.SetService()
467
468	// Set up client and server
469	h, server, uid := setupSyncTests(t, g)
470	defer h.Shutdown()
471
472	//Consume a bunch of messages to the server, and we'll sync them down
473	const numMsgs = 20
474	var refMsgs []gregor.InBandMessage
475	for i := 0; i < numMsgs; i++ {
476		msg := server.newIbm(uid)
477		refMsgs = append(refMsgs, msg.ToInBandMessage())
478		err := server.ConsumeMessage(context.TODO(), msg)
479		require.NoError(t, err)
480	}
481
482	// Sync messages down and see if we get 20
483	replayedMessages, consumedMessages := doServerSync(t, h, server)
484	checkMessages(t, "replayed messages", replayedMessages, refMsgs)
485	checkMessages(t, "consumed messages", consumedMessages, refMsgs)
486}
487
488func TestSyncNonFresh(t *testing.T) {
489	tc, g := setupGregorTest(t)
490	defer tc.Cleanup()
491	tc.G.SetService()
492
493	// Set up client and server
494	h, server, uid := setupSyncTests(t, g)
495	defer h.Shutdown()
496
497	//Consume a bunch of messages to the server, and we'll sync them down
498	const numMsgs = 6
499	const msgLimit = numMsgs / 2
500	var refMsgs []gregor.InBandMessage
501	for i := 0; i < numMsgs; i++ {
502		msg := server.newIbm(uid)
503		err := server.ConsumeMessage(context.TODO(), msg)
504		require.NoError(t, err)
505		if i < msgLimit {
506			err := broadcastMessageTesting(t, h, msg)
507			require.NoError(t, err)
508			// We end up picking up the last one in the sync, since its
509			// CTime is equal to when we start the sync, so just add it
510			if i == msgLimit-1 {
511				refMsgs = append(refMsgs, msg.ToInBandMessage())
512			}
513		} else {
514			refMsgs = append(refMsgs, msg.ToInBandMessage())
515		}
516	}
517
518	// Turn off fresh replay
519	h.firstConnect = false
520
521	// We should only get half of the messages on a non-fresh sync
522	replayedMessages, consumedMessages := doServerSync(t, h, server)
523	checkMessages(t, "replayed messages", replayedMessages, refMsgs)
524	checkMessages(t, "consumed messages", consumedMessages, refMsgs)
525}
526
527func TestSyncSaveRestoreFresh(t *testing.T) {
528	tc, g := setupGregorTest(t)
529	defer tc.Cleanup()
530	tc.G.SetService()
531
532	// Set up client and server
533	h, server, uid := setupSyncTests(t, g)
534	defer h.Shutdown()
535
536	//Consume a bunch of messages to the server, and we'll sync them down
537	const numMsgs = 6
538	const msgLimit = numMsgs / 2
539	var refReplayMsgs, refConsumeMsgs []gregor.InBandMessage
540	for i := 0; i < numMsgs; i++ {
541		msg := server.newIbm(uid)
542		err := server.ConsumeMessage(context.TODO(), msg)
543		require.NoError(t, err)
544		if i < msgLimit {
545			err := broadcastMessageTesting(t, h, msg)
546			require.NoError(t, err)
547			// We end up picking up the last one in the sync, since its
548			// CTime is equal to when we start the sync, so just add it
549			if i == msgLimit-1 {
550				refConsumeMsgs = append(refConsumeMsgs, msg.ToInBandMessage())
551			}
552		} else {
553			refConsumeMsgs = append(refConsumeMsgs, msg.ToInBandMessage())
554		}
555		refReplayMsgs = append(refReplayMsgs, msg.ToInBandMessage())
556	}
557
558	// Try saving
559	var err error
560	if err = h.gregorCli.Save(context.TODO()); err != nil {
561		t.Fatal(err)
562	}
563
564	// Create a new gregor handler, this will restore our saved state
565	h = newGregorHandler(g)
566	h.testingEvents = newTestingEvents()
567	h.Init()
568	_, err = h.resetGregorClient(context.TODO(), uid, gregor1.DeviceID{})
569	require.NoError(t, err)
570
571	// Sync from the server
572	replayedMessages, consumedMessages := doServerSync(t, h, server)
573	checkMessages(t, "replayed messages", replayedMessages, refReplayMsgs)
574	checkMessages(t, "consumed messages", consumedMessages, refConsumeMsgs)
575}
576
577func TestSyncSaveRestoreNonFresh(t *testing.T) {
578	tc, g := setupGregorTest(t)
579	defer tc.Cleanup()
580	tc.G.SetService()
581
582	// Set up client and server
583	h, server, uid := setupSyncTests(t, g)
584	defer h.Shutdown()
585
586	//Consume a bunch of messages to the server, and we'll sync them down
587	const numMsgs = 6
588	const msgLimit = numMsgs / 2
589	var refReplayMsgs, refConsumeMsgs []gregor.InBandMessage
590	for i := 0; i < numMsgs; i++ {
591		msg := server.newIbm(uid)
592		err := server.ConsumeMessage(context.TODO(), msg)
593		require.NoError(t, err)
594		if i < msgLimit {
595			err := broadcastMessageTesting(t, h, msg)
596			require.NoError(t, err)
597			// We end up picking up the last one in the sync, since its
598			// CTime is equal to when we start the sync, so just add it
599			if i == msgLimit-1 {
600				refConsumeMsgs = append(refConsumeMsgs, msg.ToInBandMessage())
601				refReplayMsgs = append(refReplayMsgs, msg.ToInBandMessage())
602			}
603		} else {
604			refConsumeMsgs = append(refConsumeMsgs, msg.ToInBandMessage())
605			refReplayMsgs = append(refReplayMsgs, msg.ToInBandMessage())
606		}
607	}
608
609	// Try saving
610	var err error
611	if err = h.gregorCli.Save(context.TODO()); err != nil {
612		t.Fatal(err)
613	}
614
615	// Create a new gregor handler, this will restore our saved state
616	h = newGregorHandler(g)
617	h.testingEvents = newTestingEvents()
618	h.Init()
619	_, err = h.resetGregorClient(context.TODO(), uid, gregor1.DeviceID{})
620	require.NoError(t, err)
621
622	// Turn off fresh replay
623	h.firstConnect = false
624
625	// Sync from the server
626	replayedMessages, consumedMessages := doServerSync(t, h, server)
627	checkMessages(t, "replayed messages", replayedMessages, refReplayMsgs)
628	checkMessages(t, "consumed messages", consumedMessages, refConsumeMsgs)
629}
630
631func TestSyncDismissal(t *testing.T) {
632	tc, g := setupGregorTest(t)
633	defer tc.Cleanup()
634	tc.G.SetService()
635
636	// Set up client and server
637	h, server, uid := setupSyncTests(t, g)
638	defer h.Shutdown()
639
640	// Consume msg
641	msg := server.newIbm(uid)
642	err := server.ConsumeMessage(context.TODO(), msg)
643	require.NoError(t, err)
644
645	// Dismiss message
646	dismissal := server.newDismissal(uid, msg)
647	err = server.ConsumeMessage(context.TODO(), dismissal.(gregor1.Message))
648	require.NoError(t, err)
649
650	// Sync from the server
651	replayedMessages, consumedMessages := doServerSync(t, h, server)
652	var refReplayMsgs, refConsumeMsgs []gregor.InBandMessage
653	checkMessages(t, "replayed messages", replayedMessages, refReplayMsgs)
654	checkMessages(t, "consumed messages", consumedMessages, refConsumeMsgs)
655}
656
657func TestMessagesAddedDuringProcessing(t *testing.T) {
658	tc, g := setupGregorTest(t)
659	defer tc.Cleanup()
660	tc.G.SetService()
661	// Set up client and server
662	h, server, uid := setupSyncTests(t, g)
663	defer h.Shutdown()
664
665	totalNumberOfMessages := 10
666	numberToDoAsync := 5
667	// create a bunch of messages to be processed
668	var msgs []gregor1.Message
669	for i := 1; i <= totalNumberOfMessages; i++ {
670		msg := server.newIbm(uid)
671		msgs = append(msgs, msg)
672	}
673
674	blockUntilDone := make(chan struct{})
675	// fire off some of them asynchronously
676	go func() {
677		for i := 0; i < numberToDoAsync; i++ {
678			err := server.ConsumeMessage(context.TODO(), msgs[i])
679			require.NoError(t, err)
680		}
681		blockUntilDone <- struct{}{}
682	}()
683	// do the rest synchronously
684	for i := numberToDoAsync; i < totalNumberOfMessages; i++ {
685		err := server.ConsumeMessage(context.TODO(), msgs[i])
686		require.NoError(t, err)
687	}
688
689	// block until everything has had a chance to get called
690	select {
691	case <-blockUntilDone:
692	case <-time.After(20 * time.Second):
693		require.Fail(t, "async messages not consumed")
694	}
695
696	// all of the messages should have been consumed
697	_, consumedMessages := doServerSync(t, h, server)
698	require.Equal(t, len(consumedMessages), totalNumberOfMessages)
699}
700
701type dummyRemoteClient struct {
702	chat1.RemoteClient
703}
704
705func (d dummyRemoteClient) GetUnreadUpdateFull(ctx context.Context, vers chat1.InboxVers) (chat1.UnreadUpdateFull, error) {
706	return chat1.UnreadUpdateFull{}, nil
707}
708
709func TestGregorBadgesIBM(t *testing.T) {
710	tc, g := setupGregorTest(t)
711	defer tc.Cleanup()
712	tc.G.SetService()
713	listener := newNlistener(t)
714	tc.G.NotifyRouter.AddListener(listener)
715
716	// Set up client and server
717	h, server, uid := setupSyncTests(t, g)
718	defer h.Shutdown()
719	h.badger = badges.NewBadger(tc.G)
720	t.Logf("client setup complete")
721
722	t.Logf("server message")
723	// One with type: created
724	msg := server.newIbm2(uid, gregor1.Category("tlf"), gregor1.Body([]byte(`{"type": "created"}`)))
725	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
726	// One with some other random type.
727	msg = server.newIbm2(uid, gregor1.Category("tlf"), gregor1.Body([]byte(`{"type": "bogusnogus"}`)))
728	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
729
730	// Sync from the server
731	t.Logf("client sync")
732	_, err := h.serverSync(context.TODO(), server, h.gregorCli, nil)
733	require.NoError(t, err)
734	t.Logf("client sync complete")
735
736	ri := func() chat1.RemoteInterface {
737		return dummyRemoteClient{RemoteClient: chat1.RemoteClient{Cli: h.cli}}
738	}
739	badgerResync(context.TODO(), t, h.badger, ri, h.gregorCli)
740
741	listener.getBadgeState(t) // skip one since resync sends 2
742	bs := listener.getBadgeState(t)
743	require.Equal(t, 1, bs.NewTlfs, "one new tlf")
744
745	t.Logf("server dismissal")
746	_ = server.newDismissal(uid, msg)
747	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
748
749	t.Logf("client sync")
750	_, err = h.serverSync(context.TODO(), server, h.gregorCli, nil)
751	require.NoError(t, err)
752	t.Logf("client sync complete")
753
754	badgerResync(context.TODO(), t, h.badger, ri, h.gregorCli)
755
756	bs = listener.getBadgeState(t)
757	require.Equal(t, 1, bs.NewTlfs, "no more badges")
758}
759
760func TestGregorTeamBadges(t *testing.T) {
761	tc, g := setupGregorTest(t)
762	defer tc.Cleanup()
763	tc.G.SetService()
764	listener := newNlistener(t)
765	tc.G.NotifyRouter.AddListener(listener)
766
767	// Set up client and server
768	h, server, uid := setupSyncTests(t, g)
769	defer h.Shutdown()
770	h.badger = badges.NewBadger(tc.G)
771	t.Logf("client setup complete")
772
773	t.Logf("server message")
774	teamID := keybase1.MakeTestTeamID(1, false)
775	fakeUID := keybase1.MakeTestUID(1)
776	msg := server.newIbm2(uid, gregor1.Category("team.newly_added_to_team"), gregor1.Body([]byte(`[{"id": "`+teamID+`","name": "teamname"}]`)))
777	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
778	msg = server.newIbm2(uid, gregor1.Category("team.request_access:"+teamID), gregor1.Body([]byte(`{"id": "`+teamID+`","username": "username"}`)))
779	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
780	msg = server.newIbm2(uid, gregor1.Category("team.member_out_from_reset"), gregor1.Body([]byte(`{"reset_user": {"uid":"`+fakeUID.String()+`","username":"alice"},"team_name": "teamname"}`)))
781	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
782
783	// Sync from the server
784	t.Logf("client sync")
785	_, err := h.serverSync(context.TODO(), server, h.gregorCli, nil)
786	require.NoError(t, err)
787	t.Logf("client sync complete")
788
789	ri := func() chat1.RemoteInterface {
790		return dummyRemoteClient{RemoteClient: chat1.RemoteClient{Cli: h.cli}}
791	}
792	badgerResync(context.TODO(), t, h.badger, ri, h.gregorCli)
793
794	listener.getBadgeState(t) // skip one since resync sends 2
795	bs := listener.getBadgeState(t)
796	require.Equal(t, 1, len(bs.NewTeams), "one new team name")
797	require.Equal(t, teamID, bs.NewTeams[0])
798	require.Equal(t, 1, bs.NewTeamAccessRequestCount, "one team access request")
799	require.Equal(t, 1, len(bs.TeamsWithResetUsers), "one team member out due to reset")
800	require.Equal(t, "teamname", bs.TeamsWithResetUsers[0].Teamname)
801	require.Equal(t, "alice", bs.TeamsWithResetUsers[0].Username)
802	require.Equal(t, msg.ToInBandMessage().Metadata().MsgID(), bs.TeamsWithResetUsers[0].Id)
803}
804
805// TestGregorBadgesOOBM doesn't actually use out of band messages.
806// Instead it feeds chat updates directly to badger. So it's a pretty weak test.
807func TestGregorBadgesOOBM(t *testing.T) {
808	tc, g := setupGregorTest(t)
809	defer tc.Cleanup()
810	tc.G.SetService()
811	listener := newNlistener(t)
812	tc.G.NotifyRouter.AddListener(listener)
813
814	// Set up client and server
815	h, _, _ := setupSyncTests(t, g)
816	defer h.Shutdown()
817	h.badger = badges.NewBadger(tc.G)
818	t.Logf("client setup complete")
819
820	t.Logf("sending first chat update")
821	h.badger.PushChatUpdate(context.TODO(), chat1.UnreadUpdate{
822		ConvID:         chat1.ConversationID(`a`),
823		UnreadMessages: 2,
824	}, 0)
825	_ = listener.getBadgeState(t)
826
827	t.Logf("sending second chat update")
828	h.badger.PushChatUpdate(context.TODO(), chat1.UnreadUpdate{
829		ConvID:         chat1.ConversationID(`b`),
830		UnreadMessages: 2,
831	}, 1)
832
833	bs := listener.getBadgeState(t)
834	require.Equal(t, 2, badgeStateStats(bs).UnreadChatConversations, "unread chat convs")
835	require.Equal(t, 4, badgeStateStats(bs).UnreadChatMessages, "unread chat messages")
836
837	t.Logf("resyncing")
838	// Instead of calling badger.Resync, reach in and twiddle the knobs.
839	h.badger.State().UpdateWithChatFull(context.TODO(), chat1.UnreadUpdateFull{
840		InboxVers: chat1.InboxVers(4),
841		Updates: []chat1.UnreadUpdate{
842			{ConvID: chat1.ConversationID(`b`), UnreadMessages: 0},
843			{ConvID: chat1.ConversationID(`c`), UnreadMessages: 3},
844		},
845		InboxSyncStatus: chat1.SyncInboxResType_CLEAR,
846	}, false)
847	err := h.badger.Send(context.TODO())
848	require.NoError(t, err)
849	bs = listener.getBadgeState(t)
850	require.Equal(t, 1, badgeStateStats(bs).UnreadChatConversations, "unread chat convs")
851	require.Equal(t, 3, badgeStateStats(bs).UnreadChatMessages, "unread chat messages")
852
853	t.Logf("clearing")
854	h.badger.Clear(context.TODO())
855	bs = listener.getBadgeState(t)
856	require.Equal(t, 0, badgeStateStats(bs).UnreadChatConversations, "unread chat convs")
857	require.Equal(t, 0, badgeStateStats(bs).UnreadChatMessages, "unread chat messages")
858}
859
860func TestSyncDismissalExistingState(t *testing.T) {
861	tc, g := setupGregorTest(t)
862	defer tc.Cleanup()
863	tc.G.SetService()
864
865	// Set up client and server
866	h, server, uid := setupSyncTests(t, g)
867	defer h.Shutdown()
868
869	var refReplayMsgs, refConsumeMsgs []gregor.InBandMessage
870
871	// Consume msg
872	msg := server.newIbm(uid)
873	err := server.ConsumeMessage(context.TODO(), msg)
874	require.NoError(t, err)
875
876	// Broadcast msg
877	err = broadcastMessageTesting(t, h, msg)
878	require.NoError(t, err)
879
880	// Consume another message but don't broadcast
881	msg2 := server.newIbm(uid)
882	err = server.ConsumeMessage(context.TODO(), msg2)
883	require.NoError(t, err)
884	refConsumeMsgs = append(refConsumeMsgs, msg2.ToInBandMessage())
885	refReplayMsgs = append(refReplayMsgs, msg2.ToInBandMessage())
886
887	// Dismiss message
888	dismissal := server.newDismissal(uid, msg)
889	err = server.ConsumeMessage(context.TODO(), dismissal.(gregor1.Message))
890	require.NoError(t, err)
891	refReplayMsgs = append(refReplayMsgs, dismissal.ToInBandMessage())
892	refConsumeMsgs = append(refConsumeMsgs, dismissal.ToInBandMessage())
893
894	// Sync from the server
895	h.firstConnect = false
896	replayedMessages, consumedMessages := doServerSync(t, h, server)
897	checkMessages(t, "replayed messages", replayedMessages, refReplayMsgs)
898	checkMessages(t, "consumed messages", consumedMessages, refConsumeMsgs)
899}
900
901func TestSyncFutureDismissals(t *testing.T) {
902	tc, g := setupGregorTest(t)
903	defer tc.Cleanup()
904	tc.G.SetService()
905
906	// Set up client and server
907	h, server, uid := setupSyncTests(t, g)
908	defer h.Shutdown()
909
910	var refReplayMsgs, refConsumeMsgs []gregor.InBandMessage
911
912	// Consume msg
913	msg := server.newIbm(uid)
914	err := server.ConsumeMessage(context.TODO(), msg)
915	require.NoError(t, err)
916	refConsumeMsgs = append(refConsumeMsgs, msg.ToInBandMessage())
917	refReplayMsgs = append(refReplayMsgs, msg.ToInBandMessage())
918
919	// Broadcast msg
920	err = broadcastMessageTesting(t, h, msg)
921	require.NoError(t, err)
922
923	// Consume another message but don't broadcast
924	msg2 := server.newIbm(uid)
925	err = server.ConsumeMessage(context.TODO(), msg2)
926	require.NoError(t, err)
927
928	// Dismiss message
929	dismissal := server.newDismissal(uid, msg2)
930	err = server.ConsumeMessage(context.TODO(), dismissal.(gregor1.Message))
931	require.NoError(t, err)
932
933	// Sync from the server
934	h.firstConnect = false
935	replayedMessages, consumedMessages := doServerSync(t, h, server)
936	checkMessages(t, "replayed messages", replayedMessages, refReplayMsgs)
937	checkMessages(t, "consumed messages", consumedMessages, refConsumeMsgs)
938}
939
940func TestBroadcastRepeat(t *testing.T) {
941	tc, g := setupGregorTest(t)
942	defer tc.Cleanup()
943
944	tc.G.SetService()
945
946	u, err := kbtest.CreateAndSignupFakeUser("gregr", tc.G)
947	if err != nil {
948		t.Fatal(err)
949	}
950	uid := gregor1.UID(u.GetUID().ToBytes())
951
952	h := newGregorHandler(g)
953	h.Init()
954	h.testingEvents = newTestingEvents()
955	_, err = h.resetGregorClient(context.TODO(), uid, gregor1.DeviceID{})
956	require.NoError(t, err)
957
958	m, err := grutils.TemplateMessage(uid)
959	if err != nil {
960		t.Fatal(err)
961	}
962	m.Ibm_.StateUpdate_.Creation_ = &gregor1.Item{
963		Category_: gregor1.Category("mike"),
964		Body_:     gregor1.Body([]byte("mike")),
965	}
966
967	m2, err := grutils.TemplateMessage(uid)
968	if err != nil {
969		t.Fatal(err)
970	}
971	m2.Ibm_.StateUpdate_.Creation_ = &gregor1.Item{
972		Category_: gregor1.Category("mike!!"),
973		Body_:     gregor1.Body([]byte("mike!!")),
974	}
975
976	err = broadcastMessageTesting(t, h, m)
977	require.NoError(t, err)
978	err = broadcastMessageTesting(t, h, m2)
979	require.NoError(t, err)
980	err = broadcastMessageTesting(t, h, m)
981	require.Error(t, err)
982	require.Equal(t, "ignored repeat message", err.Error())
983}
984
985type BadgeStateStats struct {
986	UnreadChatConversations int
987	UnreadChatMessages      int
988}
989
990func badgeStateStats(bs keybase1.BadgeState) (res BadgeStateStats) {
991	for _, c := range bs.Conversations {
992		res.UnreadChatMessages += c.UnreadMessages
993		if c.UnreadMessages > 0 {
994			res.UnreadChatConversations++
995		}
996	}
997	return
998}
999
1000func TestLocalDismissals(t *testing.T) {
1001	tc, g := setupGregorTest(t)
1002	defer tc.Cleanup()
1003	tc.G.SetService()
1004
1005	// Set up client and server
1006	h, server, uid := setupSyncTests(t, g)
1007	defer h.Shutdown()
1008
1009	var refReplayMsgs []gregor.InBandMessage
1010	var refConsumeMsgs []gregor.InBandMessage
1011	msg := server.newIbm(uid)
1012	require.NoError(t, server.ConsumeMessage(context.TODO(), msg))
1013	refConsumeMsgs = append(refConsumeMsgs, msg.ToInBandMessage())
1014	refReplayMsgs = append(refReplayMsgs, msg.ToInBandMessage())
1015
1016	lmsg := server.newIbm(uid)
1017	require.NoError(t, server.ConsumeMessage(context.TODO(), lmsg))
1018	refConsumeMsgs = append(refConsumeMsgs, lmsg.ToInBandMessage())
1019
1020	require.NoError(t, h.LocalDismissItem(context.TODO(), lmsg.ToInBandMessage().Metadata().MsgID()))
1021
1022	replayedMessages, consumedMessages := doServerSync(t, h, server)
1023	checkMessages(t, "replayed messages", replayedMessages, refReplayMsgs)
1024	checkMessages(t, "consumed messages", consumedMessages, refConsumeMsgs)
1025
1026	dis := server.newDismissal(uid, lmsg)
1027	require.NoError(t, server.ConsumeMessage(context.TODO(), dis.(gregor1.Message)))
1028	require.NoError(t, broadcastMessageTesting(t, h, dis.(gregor1.Message)))
1029
1030	gcli, err := h.getGregorCli()
1031	require.NoError(t, err)
1032	lds, err := gcli.Sm.LocalDismissals(context.TODO(), uid)
1033	require.NoError(t, err)
1034	require.Zero(t, len(lds))
1035}
1036
1037type flakeyIncomingClient struct {
1038	gregor1.IncomingInterface
1039
1040	offline bool
1041	client  func() gregor1.IncomingInterface
1042}
1043
1044func newFlakeyIncomingClient(client func() gregor1.IncomingInterface) flakeyIncomingClient {
1045	return flakeyIncomingClient{
1046		client: client,
1047	}
1048}
1049
1050func (f flakeyIncomingClient) ConsumeMessage(ctx context.Context, m gregor1.Message) error {
1051	if f.offline {
1052		return errors.New("offline")
1053	}
1054	return f.client().ConsumeMessage(ctx, m)
1055}
1056
1057func TestOfflineConsume(t *testing.T) {
1058	tc, g := setupGregorTest(t)
1059	defer tc.Cleanup()
1060	tc.G.SetService()
1061	h, server, uid := setupSyncTests(t, g)
1062	defer h.Shutdown()
1063
1064	fclient := newFlakeyIncomingClient(func() gregor1.IncomingInterface { return server })
1065	fc := clockwork.NewFakeClock()
1066	client := grclient.NewClient(uid, nil, func() gregor.StateMachine {
1067		return storage.NewMemEngine(gregor1.ObjFactory{}, clockwork.NewRealClock(), tc.G.GetLog())
1068	}, storage.NewLocalDB(tc.G), func() gregor1.IncomingInterface { return fclient }, tc.G.GetLog(), fc)
1069	tev := grclient.NewTestingEvents()
1070	client.TestingEvents = tev
1071	h.gregorCli = client
1072
1073	// Try to consume offline
1074	t.Logf("offline")
1075	fclient.offline = true
1076	msg := server.newIbm(uid)
1077	require.NoError(t, client.ConsumeMessage(context.TODO(), msg))
1078	serverState, err := server.State(context.TODO(), gregor1.StateArg{
1079		Uid: uid,
1080	})
1081	require.NoError(t, err)
1082	require.Zero(t, len(serverState.Items_))
1083	clientState, err := client.StateMachineState(context.TODO(), gregor1.TimeOrOffset{}, true)
1084	require.NoError(t, err)
1085	items, err := clientState.Items()
1086	require.NoError(t, err)
1087	require.Equal(t, 1, len(items))
1088	require.Equal(t, msg.ToInBandMessage().Metadata().MsgID().String(),
1089		items[0].Metadata().MsgID().String())
1090	select {
1091	case <-tev.OutboxSend:
1092		require.Fail(t, "should not have sent")
1093	default:
1094	}
1095
1096	// Come back online
1097	t.Logf("online")
1098	fclient.offline = false
1099	fc.Advance(10 * time.Minute)
1100	select {
1101	case msg := <-tev.OutboxSend:
1102		require.Equal(t, msg.ToInBandMessage().Metadata().MsgID().String(),
1103			items[0].Metadata().MsgID().String())
1104	case <-time.After(20 * time.Second):
1105		require.Fail(t, "no send")
1106	}
1107	serverState, err = server.State(context.TODO(), gregor1.StateArg{
1108		Uid: uid,
1109	})
1110	require.NoError(t, err)
1111	require.NoError(t, broadcastMessageTesting(t, h, msg))
1112	require.Equal(t, 1, len(serverState.Items_))
1113	require.Equal(t, msg.ToInBandMessage().Metadata().MsgID().String(),
1114		serverState.Items_[0].Metadata().MsgID().String())
1115	clientState, err = client.StateMachineState(context.TODO(), gregor1.TimeOrOffset{}, true)
1116	require.NoError(t, err)
1117	items, err = clientState.Items()
1118	require.NoError(t, err)
1119	require.Equal(t, 1, len(items))
1120	require.Equal(t, msg.ToInBandMessage().Metadata().MsgID().String(),
1121		items[0].Metadata().MsgID().String())
1122
1123}
1124
1125func badgerResync(ctx context.Context, t testing.TB, b *badges.Badger, chatRemote func() chat1.RemoteInterface,
1126	gcli *grclient.Client) {
1127	iboxVersion, err := b.GetInboxVersionForTest(ctx)
1128	require.NoError(t, err)
1129	b.G().Log.Debug("Badger: Resync(): using inbox version: %v", iboxVersion)
1130	update, err := chatRemote().GetUnreadUpdateFull(ctx, iboxVersion)
1131	require.NoError(t, err)
1132
1133	state, err := gcli.StateMachineState(ctx, nil, false)
1134	require.NoError(t, err)
1135
1136	b.PushChatFullUpdate(ctx, update)
1137	b.PushState(ctx, state)
1138}
1139