github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/chat/flipmanager_test.go (about) 1 package chat 2 3 import ( 4 "context" 5 "fmt" 6 "strings" 7 "testing" 8 "time" 9 10 "github.com/keybase/client/go/chat/flip" 11 "github.com/keybase/client/go/chat/globals" 12 "github.com/keybase/client/go/chat/types" 13 "github.com/keybase/client/go/externalstest" 14 "github.com/keybase/client/go/kbtest" 15 "github.com/keybase/client/go/protocol/chat1" 16 "github.com/keybase/client/go/protocol/gregor1" 17 "github.com/keybase/clockwork" 18 "github.com/stretchr/testify/require" 19 ) 20 21 func consumeFlipToResult(t *testing.T, ui *kbtest.ChatUI, listener *serverChatListener, 22 gameID chat1.FlipGameIDStr, numUsers int) string { 23 timeout := 20 * time.Second 24 consumeNewMsgRemote(t, listener, chat1.MessageType_FLIP) // host msg 25 for { 26 select { 27 case updates := <-ui.CoinFlipUpdates: 28 require.Equal(t, 1, len(updates)) 29 t.Logf("update: %v gameID: %s", updates[0].Phase, updates[0].GameID) 30 if updates[0].Phase == chat1.UICoinFlipPhase_COMPLETE { 31 if updates[0].GameID != gameID { 32 // it is possible for a game to produce more than one complete update 33 // so if we get one for a different game, just skip it 34 t.Logf("skipping complete: looking: %s found: %s", gameID, updates[0].GameID) 35 continue 36 } 37 require.Equal(t, numUsers, len(updates[0].Participants)) 38 return updates[0].ResultText 39 } 40 case <-time.After(timeout): 41 require.Fail(t, "no complete") 42 } 43 } 44 } 45 func assertNoFlip(t *testing.T, ui *kbtest.ChatUI) { 46 select { 47 case <-ui.CoinFlipUpdates: 48 require.Fail(t, "unexpected coinflip update") 49 default: 50 } 51 } 52 53 func TestFlipManagerStartFlip(t *testing.T) { 54 t.Skip() 55 runWithMemberTypes(t, func(mt chat1.ConversationMembersType) { 56 runWithEphemeral(t, mt, func(ephemeralLifetime *gregor1.DurationSec) { 57 ctc := makeChatTestContext(t, "FlipManagerStartFlip", 3) 58 defer ctc.cleanup() 59 60 users := ctc.users() 61 numUsers := 3 62 flip.DefaultCommitmentWindowMsec = 2000 63 64 var ui0, ui1, ui2 *kbtest.ChatUI 65 ui0 = kbtest.NewChatUI() 66 ui1 = kbtest.NewChatUI() 67 ui2 = kbtest.NewChatUI() 68 ctc.as(t, users[0]).h.mockChatUI = ui0 69 ctc.as(t, users[1]).h.mockChatUI = ui1 70 ctc.as(t, users[2]).h.mockChatUI = ui2 71 ctc.world.Tcs[users[0].Username].G.UIRouter = kbtest.NewMockUIRouter(ui0) 72 ctc.world.Tcs[users[1].Username].G.UIRouter = kbtest.NewMockUIRouter(ui1) 73 ctc.world.Tcs[users[2].Username].G.UIRouter = kbtest.NewMockUIRouter(ui2) 74 listener0 := newServerChatListener() 75 listener1 := newServerChatListener() 76 listener2 := newServerChatListener() 77 ctc.as(t, users[0]).h.G().NotifyRouter.AddListener(listener0) 78 ctc.as(t, users[1]).h.G().NotifyRouter.AddListener(listener1) 79 ctc.as(t, users[2]).h.G().NotifyRouter.AddListener(listener2) 80 81 t.Logf("uid0: %s", users[0].GetUID()) 82 t.Logf("uid1: %s", users[1].GetUID()) 83 t.Logf("uid2: %s", users[2].GetUID()) 84 conv := mustCreateConversationForTest(t, ctc, users[0], chat1.TopicType_CHAT, 85 mt, ctc.as(t, users[1]).user(), ctc.as(t, users[2]).user()) 86 consumeNewConversation(t, listener0, conv.Id) 87 consumeNewConversation(t, listener1, conv.Id) 88 consumeNewConversation(t, listener2, conv.Id) 89 var policy *chat1.RetentionPolicy 90 if ephemeralLifetime != nil { 91 p := chat1.NewRetentionPolicyWithEphemeral(chat1.RpEphemeral{Age: *ephemeralLifetime}) 92 policy = &p 93 mustSetConvRetentionLocal(t, ctc, users[0], conv.Id, p) 94 consumeNewMsgRemote(t, listener0, chat1.MessageType_SYSTEM) 95 consumeNewMsgRemote(t, listener1, chat1.MessageType_SYSTEM) 96 consumeNewMsgRemote(t, listener2, chat1.MessageType_SYSTEM) 97 } 98 99 var flipMsgs []chat1.UIMessage 100 expectedDevConvs := 0 101 // bool 102 expectedDevConvs++ 103 mustPostLocalForTest(t, ctc, users[0], conv, 104 chat1.NewMessageBodyWithText(chat1.MessageText{ 105 Body: "/flip", 106 })) 107 flipMsg := consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 108 flipMsgs = append(flipMsgs, flipMsg) 109 require.True(t, flipMsg.IsValid()) 110 require.NotNil(t, flipMsg.Valid().FlipGameID) 111 gameID := *flipMsg.Valid().FlipGameID 112 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 113 consumeNewMsgRemote(t, listener2, chat1.MessageType_FLIP) 114 res0 := consumeFlipToResult(t, ui0, listener0, gameID, numUsers) 115 t.Logf("res0 (coin): %s", res0) 116 require.True(t, res0 == "HEADS" || res0 == "TAILS") 117 res1 := consumeFlipToResult(t, ui1, listener1, gameID, numUsers) 118 require.Equal(t, res0, res1) 119 res2 := consumeFlipToResult(t, ui2, listener2, gameID, numUsers) 120 require.Equal(t, res0, res2) 121 122 // limit 123 expectedDevConvs++ 124 mustPostLocalForTest(t, ctc, users[0], conv, 125 chat1.NewMessageBodyWithText(chat1.MessageText{ 126 Body: "/flip 10", 127 })) 128 flipMsg = consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 129 flipMsgs = append(flipMsgs, flipMsg) 130 require.True(t, flipMsg.IsValid()) 131 require.NotNil(t, flipMsg.Valid().FlipGameID) 132 gameID = *flipMsg.Valid().FlipGameID 133 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 134 consumeNewMsgRemote(t, listener2, chat1.MessageType_FLIP) 135 res0 = consumeFlipToResult(t, ui0, listener0, gameID, numUsers) 136 found := false 137 t.Logf("res0 (limit): %s", res0) 138 for i := 1; i <= 10; i++ { 139 if res0 == fmt.Sprintf("%d", i) { 140 found = true 141 break 142 } 143 } 144 require.True(t, found) 145 res1 = consumeFlipToResult(t, ui1, listener1, gameID, numUsers) 146 require.Equal(t, res0, res1) 147 res2 = consumeFlipToResult(t, ui2, listener2, gameID, numUsers) 148 require.Equal(t, res0, res2) 149 150 // range 151 expectedDevConvs++ 152 mustPostLocalForTest(t, ctc, users[0], conv, 153 chat1.NewMessageBodyWithText(chat1.MessageText{ 154 Body: "/flip 10..15", 155 })) 156 flipMsg = consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 157 flipMsgs = append(flipMsgs, flipMsg) 158 require.True(t, flipMsg.IsValid()) 159 require.NotNil(t, flipMsg.Valid().FlipGameID) 160 gameID = *flipMsg.Valid().FlipGameID 161 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 162 consumeNewMsgRemote(t, listener2, chat1.MessageType_FLIP) 163 res0 = consumeFlipToResult(t, ui0, listener0, gameID, numUsers) 164 t.Logf("res0 (range): %s", res0) 165 found = false 166 for i := 10; i <= 15; i++ { 167 if res0 == fmt.Sprintf("%d", i) { 168 found = true 169 break 170 } 171 } 172 require.True(t, found) 173 res1 = consumeFlipToResult(t, ui1, listener1, gameID, numUsers) 174 require.Equal(t, res0, res1) 175 res2 = consumeFlipToResult(t, ui2, listener2, gameID, numUsers) 176 require.Equal(t, res0, res2) 177 178 // shuffle 179 ref := []string{"mike", "karen", "lisa", "sara", "anna"} 180 refMap := make(map[string]bool) 181 for _, r := range ref { 182 refMap[r] = true 183 } 184 expectedDevConvs++ 185 mustPostLocalForTest(t, ctc, users[0], conv, 186 chat1.NewMessageBodyWithText(chat1.MessageText{ 187 Body: fmt.Sprintf("/flip %s", strings.Join(ref, ",")), 188 })) 189 flipMsg = consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 190 flipMsgs = append(flipMsgs, flipMsg) 191 require.True(t, flipMsg.IsValid()) 192 require.NotNil(t, flipMsg.Valid().FlipGameID) 193 gameID = *flipMsg.Valid().FlipGameID 194 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 195 consumeNewMsgRemote(t, listener2, chat1.MessageType_FLIP) 196 res0 = consumeFlipToResult(t, ui0, listener0, gameID, numUsers) 197 t.Logf("res0 (shuffle): %s", res0) 198 toks := strings.Split(res0, ",") 199 for _, t := range toks { 200 delete(refMap, strings.Trim(t, " ")) 201 } 202 require.Zero(t, len(refMap)) 203 require.True(t, found) 204 res1 = consumeFlipToResult(t, ui1, listener1, gameID, numUsers) 205 require.Equal(t, res0, res1) 206 res2 = consumeFlipToResult(t, ui2, listener2, gameID, numUsers) 207 require.Equal(t, res0, res2) 208 209 uid := users[0].User.GetUID().ToBytes() 210 ttype := chat1.TopicType_DEV 211 ctx := ctc.as(t, users[0]).startCtx 212 ibox, _, err := ctc.as(t, users[0]).h.G().InboxSource.Read(ctx, uid, 213 types.ConversationLocalizerBlocking, types.InboxSourceDataSourceAll, nil, 214 &chat1.GetInboxLocalQuery{ 215 TopicType: &ttype, 216 }) 217 require.NoError(t, err) 218 numConvs := 0 219 for _, conv := range ibox.Convs { 220 if strings.HasPrefix(conv.Info.TopicName, gameIDTopicNamePrefix) { 221 numConvs++ 222 require.Equal(t, policy, conv.ConvRetention) 223 } 224 } 225 require.Equal(t, expectedDevConvs, numConvs) 226 for _, flipMsg := range flipMsgs { 227 mustDeleteMsg(ctx, t, ctc, users[0], conv, flipMsg.GetMessageID()) 228 consumeNewMsgRemote(t, listener1, chat1.MessageType_DELETE) 229 consumeNewMsgRemote(t, listener2, chat1.MessageType_DELETE) 230 } 231 ibox, _, err = ctc.as(t, users[0]).h.G().InboxSource.Read(ctx, uid, 232 types.ConversationLocalizerBlocking, types.InboxSourceDataSourceAll, nil, 233 &chat1.GetInboxLocalQuery{ 234 TopicType: &ttype, 235 }) 236 require.NoError(t, err) 237 require.Zero(t, len(ibox.Convs)) 238 }) 239 }) 240 } 241 242 func TestFlipManagerChannelFlip(t *testing.T) { 243 // ensure only members of a channel are included in the flip 244 runWithMemberTypes(t, func(mt chat1.ConversationMembersType) { 245 switch mt { 246 case chat1.ConversationMembersType_TEAM: 247 default: 248 return 249 } 250 ctc := makeChatTestContext(t, "FlipManagerChannelFlip", 3) 251 defer ctc.cleanup() 252 253 users := ctc.users() 254 flip.DefaultCommitmentWindowMsec = 500 255 256 var ui0, ui1, ui2 *kbtest.ChatUI 257 ui0 = kbtest.NewChatUI() 258 ui1 = kbtest.NewChatUI() 259 ui2 = kbtest.NewChatUI() 260 ctc.as(t, users[0]).h.mockChatUI = ui0 261 ctc.as(t, users[1]).h.mockChatUI = ui1 262 ctc.as(t, users[2]).h.mockChatUI = ui2 263 ctc.world.Tcs[users[0].Username].G.UIRouter = kbtest.NewMockUIRouter(ui0) 264 ctc.world.Tcs[users[1].Username].G.UIRouter = kbtest.NewMockUIRouter(ui1) 265 ctc.world.Tcs[users[2].Username].G.UIRouter = kbtest.NewMockUIRouter(ui2) 266 listener0 := newServerChatListener() 267 listener1 := newServerChatListener() 268 listener2 := newServerChatListener() 269 ctc.as(t, users[0]).h.G().NotifyRouter.AddListener(listener0) 270 ctc.as(t, users[1]).h.G().NotifyRouter.AddListener(listener1) 271 ctc.as(t, users[2]).h.G().NotifyRouter.AddListener(listener2) 272 273 conv := mustCreateConversationForTest(t, ctc, users[0], chat1.TopicType_CHAT, 274 mt, ctc.as(t, users[1]).user(), ctc.as(t, users[2]).user()) 275 consumeNewConversation(t, listener0, conv.Id) 276 consumeNewConversation(t, listener1, conv.Id) 277 consumeNewConversation(t, listener2, conv.Id) 278 279 topicName := "channel-1" 280 channel := mustCreateChannelForTest(t, ctc, users[0], chat1.TopicType_CHAT, 281 &topicName, mt, ctc.as(t, users[1]).user(), ctc.as(t, users[2]).user()) 282 consumeNewMsgRemote(t, listener0, chat1.MessageType_JOIN) 283 consumeNewMsgRemote(t, listener0, chat1.MessageType_SYSTEM) 284 consumeNewMsgRemote(t, listener0, chat1.MessageType_SYSTEM) 285 consumeNewMsgRemote(t, listener1, chat1.MessageType_SYSTEM) 286 consumeNewMsgRemote(t, listener1, chat1.MessageType_SYSTEM) 287 consumeNewMsgRemote(t, listener2, chat1.MessageType_SYSTEM) 288 consumeNewMsgRemote(t, listener2, chat1.MessageType_SYSTEM) 289 290 mustJoinConversationByID(t, ctc, users[1], channel.Id) 291 consumeNewMsgRemote(t, listener0, chat1.MessageType_JOIN) 292 consumeNewMsgRemote(t, listener1, chat1.MessageType_JOIN) 293 mustJoinConversationByID(t, ctc, users[2], channel.Id) 294 _, err := ctc.as(t, users[2]).chatLocalHandler().LeaveConversationLocal( 295 ctc.as(t, users[0]).startCtx, channel.Id) 296 require.NoError(t, err) 297 consumeNewMsgRemote(t, listener0, chat1.MessageType_JOIN) 298 consumeNewMsgRemote(t, listener1, chat1.MessageType_JOIN) 299 consumeNewMsgRemote(t, listener2, chat1.MessageType_JOIN) 300 consumeNewMsgRemote(t, listener0, chat1.MessageType_LEAVE) 301 consumeNewMsgRemote(t, listener1, chat1.MessageType_LEAVE) 302 303 mustPostLocalForTest(t, ctc, users[0], channel, 304 chat1.NewMessageBodyWithText(chat1.MessageText{ 305 Body: "/flip", 306 })) 307 flipMsg := consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 308 require.True(t, flipMsg.IsValid()) 309 require.NotNil(t, flipMsg.Valid().FlipGameID) 310 gameID := *flipMsg.Valid().FlipGameID 311 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 312 res0 := consumeFlipToResult(t, ui0, listener0, gameID, 2) 313 require.True(t, res0 == "HEADS" || res0 == "TAILS") 314 res1 := consumeFlipToResult(t, ui1, listener1, gameID, 2) 315 require.Equal(t, res0, res1) 316 assertNoFlip(t, ui2) 317 }) 318 } 319 320 func TestFlipManagerParseEdges(t *testing.T) { 321 tc := externalstest.SetupTest(t, "flip", 0) 322 defer tc.Cleanup() 323 324 g := globals.NewContext(tc.G, &globals.ChatContext{}) 325 fm := NewFlipManager(g, nil) 326 testCase := func(text string, ftyp flip.FlipType, refMetadata flipTextMetadata) { 327 start, metadata := fm.startFromText(text, nil) 328 ft, err := start.Params.T() 329 require.NoError(t, err) 330 require.Equal(t, ftyp, ft) 331 require.Equal(t, refMetadata, metadata) 332 } 333 deck := "2♠️,3♠️,4♠️,5♠️,6♠️,7♠️,8♠️,9♠️,10♠️,J♠️,Q♠️,K♠️,A♠️,2♣️,3♣️,4♣️,5♣️,6♣️,7♣️,8♣️,9♣️,10♣️,J♣️,Q♣️,K♣️,A♣️,2♦️,3♦️,4♦️,5♦️,6♦️,7♦️,8♦️,9♦️,10♦️,J♦️,Q♦️,K♦️,A♦️,2♥️,3♥️,4♥️,5♥️,6♥️,7♥️,8♥️,9♥️,10♥️,J♥️,Q♥️,K♥️,A♥️" 334 cards := strings.Split(deck, ",") 335 testCase("/flip 10", flip.FlipType_BIG, flipTextMetadata{LowerBound: "1"}) 336 testCase("/flip 0", flip.FlipType_SHUFFLE, flipTextMetadata{ShuffleItems: []string{"0"}}) 337 testCase("/flip -1", flip.FlipType_SHUFFLE, flipTextMetadata{ShuffleItems: []string{"-1"}}) 338 testCase("/flip 1..5", flip.FlipType_BIG, flipTextMetadata{LowerBound: "1"}) 339 testCase("/flip -20..20", flip.FlipType_BIG, flipTextMetadata{LowerBound: "-20"}) 340 testCase("/flip -20..20,mike", flip.FlipType_SHUFFLE, 341 flipTextMetadata{ShuffleItems: []string{"-20..20", "mike"}}) 342 testCase("/flip 1..1", flip.FlipType_BIG, flipTextMetadata{LowerBound: "1"}) 343 testCase("/flip 1..0", flip.FlipType_SHUFFLE, flipTextMetadata{ShuffleItems: []string{"1..0"}}) 344 testCase("/flip mike, karen, jim", flip.FlipType_SHUFFLE, 345 flipTextMetadata{ShuffleItems: []string{"mike", "karen", "jim"}}) 346 testCase("/flip mike, jim bob j , jim", flip.FlipType_SHUFFLE, 347 flipTextMetadata{ShuffleItems: []string{"mike", "jim bob j", "jim"}}) 348 testCase("/flip 10...20", flip.FlipType_SHUFFLE, flipTextMetadata{ShuffleItems: []string{"10...20"}}) 349 testCase("/flip 1,0", flip.FlipType_SHUFFLE, flipTextMetadata{ShuffleItems: []string{"1", "0"}}) 350 testCase("/flip 1,0", flip.FlipType_SHUFFLE, flipTextMetadata{ShuffleItems: []string{"1", "0"}}) 351 testCase("/flip cards", flip.FlipType_SHUFFLE, flipTextMetadata{ 352 ShuffleItems: cards, 353 DeckShuffle: true, 354 }) 355 testCase("/flip cards 5 mikem, joshblum, chris", flip.FlipType_SHUFFLE, flipTextMetadata{ 356 ShuffleItems: cards, 357 DeckShuffle: false, 358 HandCardCount: 5, 359 HandTargets: []string{"mikem", "joshblum", "chris"}, 360 }) 361 testCase("/flip cards 5 mike maxim, lisa maxim, anna ", flip.FlipType_SHUFFLE, flipTextMetadata{ 362 ShuffleItems: cards, 363 DeckShuffle: false, 364 HandCardCount: 5, 365 HandTargets: []string{"mike maxim", "lisa maxim", "anna"}, 366 }) 367 testCase("/flip cards 5 mikem, , , joshblum, chris", flip.FlipType_SHUFFLE, 368 flipTextMetadata{ 369 ShuffleItems: cards, 370 DeckShuffle: false, 371 HandCardCount: 5, 372 HandTargets: []string{"mikem", "joshblum", "chris"}, 373 }) 374 testCase("/flip cards 5", flip.FlipType_SHUFFLE, flipTextMetadata{ 375 ShuffleItems: cards, 376 DeckShuffle: true, 377 }) 378 testCase("/flip cards -5 chris mike", flip.FlipType_SHUFFLE, flipTextMetadata{ 379 ShuffleItems: cards, 380 DeckShuffle: true, 381 }) 382 } 383 384 func TestFlipManagerLoadFlip(t *testing.T) { 385 t.Skip() 386 runWithMemberTypes(t, func(mt chat1.ConversationMembersType) { 387 ctc := makeChatTestContext(t, "FlipManager", 2) 388 defer ctc.cleanup() 389 390 users := ctc.users() 391 ui0 := kbtest.NewChatUI() 392 ui1 := kbtest.NewChatUI() 393 ctc.as(t, users[0]).h.mockChatUI = ui0 394 ctc.as(t, users[1]).h.mockChatUI = ui1 395 ctc.world.Tcs[users[0].Username].G.UIRouter = kbtest.NewMockUIRouter(ui0) 396 ctc.world.Tcs[users[1].Username].G.UIRouter = kbtest.NewMockUIRouter(ui1) 397 ctx := ctc.as(t, users[0]).startCtx 398 tc := ctc.world.Tcs[users[0].Username] 399 uid := users[0].User.GetUID().ToBytes() 400 listener0 := newServerChatListener() 401 listener1 := newServerChatListener() 402 ctc.as(t, users[0]).h.G().NotifyRouter.AddListener(listener0) 403 ctc.as(t, users[1]).h.G().NotifyRouter.AddListener(listener1) 404 flip.DefaultCommitmentWindowMsec = 500 405 timeout := 20 * time.Second 406 ctc.world.Tcs[users[0].Username].ChatG.Syncer.(*Syncer).isConnected = true 407 408 conv := mustCreateConversationForTest(t, ctc, users[0], chat1.TopicType_CHAT, mt, 409 ctc.as(t, users[1]).user()) 410 mustPostLocalForTest(t, ctc, users[0], conv, 411 chat1.NewMessageBodyWithText(chat1.MessageText{ 412 Body: "/flip", 413 })) 414 flipMsg := consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 415 require.True(t, flipMsg.IsValid()) 416 require.NotNil(t, flipMsg.Valid().FlipGameID) 417 strGameID := *flipMsg.Valid().FlipGameID 418 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 419 res := consumeFlipToResult(t, ui0, listener0, strGameID, 2) 420 require.True(t, res == "HEADS" || res == "TAILS") 421 res1 := consumeFlipToResult(t, ui1, listener1, strGameID, 2) 422 require.Equal(t, res, res1) 423 424 hostMsg, err := tc.Context().ConvSource.GetMessage(ctx, conv.Id, uid, 2, nil, nil, true) 425 require.NoError(t, err) 426 require.True(t, hostMsg.IsValid()) 427 body := hostMsg.Valid().MessageBody 428 require.True(t, body.IsType(chat1.MessageType_FLIP)) 429 gameID := body.Flip().GameID 430 431 testLoadFlip := func() { 432 tc.Context().CoinFlipManager.LoadFlip(ctx, uid, conv.Id, hostMsg.GetMessageID(), 433 body.Flip().FlipConvID, gameID) 434 select { 435 case updates := <-ui0.CoinFlipUpdates: 436 require.Equal(t, 1, len(updates)) 437 require.Equal(t, chat1.UICoinFlipPhase_COMPLETE, updates[0].Phase) 438 require.Equal(t, res, updates[0].ResultText) 439 case <-time.After(timeout): 440 require.Fail(t, "no updates") 441 } 442 } 443 testLoadFlip() 444 err = tc.Context().ConvSource.Clear(ctx, conv.Id, uid, nil) 445 require.NoError(t, err) 446 tc.Context().CoinFlipManager.(*FlipManager).clearGameCache() 447 testLoadFlip() 448 }) 449 } 450 451 func TestFlipManagerRateLimit(t *testing.T) { 452 t.Skip() 453 ctc := makeChatTestContext(t, "TestFlipManagerRateLimit", 2) 454 defer ctc.cleanup() 455 users := ctc.users() 456 useRemoteMock = false 457 defer func() { useRemoteMock = true }() 458 459 ui0 := kbtest.NewChatUI() 460 ui1 := kbtest.NewChatUI() 461 ctc.as(t, users[0]).h.mockChatUI = ui0 462 ctc.as(t, users[1]).h.mockChatUI = ui1 463 ctc.world.Tcs[users[0].Username].G.UIRouter = kbtest.NewMockUIRouter(ui0) 464 ctc.world.Tcs[users[1].Username].G.UIRouter = kbtest.NewMockUIRouter(ui1) 465 listener0 := newServerChatListener() 466 listener1 := newServerChatListener() 467 ctc.as(t, users[0]).h.G().NotifyRouter.AddListener(listener0) 468 ctc.as(t, users[1]).h.G().NotifyRouter.AddListener(listener1) 469 flip.DefaultCommitmentWindowMsec = 500 470 tc := ctc.world.Tcs[users[0].Username] 471 tc1 := ctc.world.Tcs[users[1].Username] 472 clock := clockwork.NewFakeClock() 473 flipmgr := tc.Context().CoinFlipManager.(*FlipManager) 474 flipmgr1 := tc1.Context().CoinFlipManager.(*FlipManager) 475 flipmgr.clock = clock 476 flipmgr1.clock = clock 477 flipmgr.testingServerClock = clock 478 flipmgr1.testingServerClock = clock 479 flipmgr.maxConvParticipations = 1 480 <-flipmgr.Stop(context.TODO()) 481 <-flipmgr1.Stop(context.TODO()) 482 flipmgr.Start(context.TODO(), gregor1.UID(users[0].GetUID().ToBytes())) 483 flipmgr1.Start(context.TODO(), gregor1.UID(users[1].GetUID().ToBytes())) 484 simRealClock := func(stopCh chan struct{}) { 485 t := time.NewTicker(100 * time.Millisecond) 486 for { 487 select { 488 case <-t.C: 489 clock.Advance(100 * time.Millisecond) 490 case <-stopCh: 491 return 492 } 493 } 494 } 495 t.Logf("uid0: %s", users[0].GetUID()) 496 t.Logf("uid1: %s", users[1].GetUID()) 497 498 conv := mustCreateConversationForTest(t, ctc, users[0], chat1.TopicType_CHAT, 499 chat1.ConversationMembersType_IMPTEAMNATIVE, ctc.as(t, users[1]).user()) 500 mustPostLocalForTest(t, ctc, users[0], conv, 501 chat1.NewMessageBodyWithText(chat1.MessageText{ 502 Body: "/flip", 503 })) 504 flipMsg := consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 505 require.True(t, flipMsg.IsValid()) 506 require.NotNil(t, flipMsg.Valid().FlipGameID) 507 gameID := *flipMsg.Valid().FlipGameID 508 t.Logf("gameID: %s", gameID) 509 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 510 stopCh := make(chan struct{}) 511 go simRealClock(stopCh) 512 res := consumeFlipToResult(t, ui0, listener0, gameID, 2) 513 require.True(t, res == "HEADS" || res == "TAILS") 514 res1 := consumeFlipToResult(t, ui1, listener1, gameID, 2) 515 require.Equal(t, res, res1) 516 close(stopCh) 517 518 clock.Advance(time.Minute) 519 mustPostLocalForTest(t, ctc, users[1], conv, 520 chat1.NewMessageBodyWithText(chat1.MessageText{ 521 Body: "/flip", 522 })) 523 select { 524 case <-ui0.CoinFlipUpdates: 525 require.Fail(t, "no update for 0") 526 default: 527 } 528 flipMsg = consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 529 require.True(t, flipMsg.IsValid()) 530 require.NotNil(t, flipMsg.Valid().FlipGameID) 531 gameID = *flipMsg.Valid().FlipGameID 532 t.Logf("gameID: %s", gameID) 533 stopCh = make(chan struct{}) 534 go simRealClock(stopCh) 535 consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) // get host msg 536 res = consumeFlipToResult(t, ui1, listener1, gameID, 1) 537 require.True(t, res == "HEADS" || res == "TAILS") 538 close(stopCh) 539 540 clock.Advance(10 * time.Minute) 541 mustPostLocalForTest(t, ctc, users[1], conv, 542 chat1.NewMessageBodyWithText(chat1.MessageText{ 543 Body: "/flip", 544 })) 545 flipMsg = consumeNewMsgRemote(t, listener0, chat1.MessageType_FLIP) 546 require.True(t, flipMsg.IsValid()) 547 require.NotNil(t, flipMsg.Valid().FlipGameID) 548 gameID = *flipMsg.Valid().FlipGameID 549 t.Logf("gameID: %s", gameID) 550 consumeNewMsgRemote(t, listener1, chat1.MessageType_FLIP) 551 stopCh = make(chan struct{}) 552 go simRealClock(stopCh) 553 res = consumeFlipToResult(t, ui0, listener0, gameID, 2) 554 require.True(t, res == "HEADS" || res == "TAILS") 555 res1 = consumeFlipToResult(t, ui1, listener1, gameID, 2) 556 require.Equal(t, res, res1) 557 close(stopCh) 558 559 }