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