github.com/status-im/status-go@v1.1.0/protocol/communities_messenger_test.go (about) 1 package protocol 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/ecdsa" 7 "crypto/tls" 8 "encoding/base64" 9 "encoding/json" 10 "errors" 11 "fmt" 12 "io/ioutil" 13 "net/http" 14 "net/url" 15 "os" 16 "reflect" 17 "strings" 18 "testing" 19 "time" 20 21 "github.com/golang/protobuf/proto" 22 "github.com/stretchr/testify/suite" 23 "go.uber.org/zap" 24 25 gethcommon "github.com/ethereum/go-ethereum/common" 26 27 "github.com/status-im/status-go/eth-node/crypto" 28 "github.com/status-im/status-go/eth-node/types" 29 "github.com/status-im/status-go/images" 30 "github.com/status-im/status-go/multiaccounts/accounts" 31 multiaccountscommon "github.com/status-im/status-go/multiaccounts/common" 32 "github.com/status-im/status-go/protocol/common" 33 "github.com/status-im/status-go/protocol/communities" 34 "github.com/status-im/status-go/protocol/discord" 35 "github.com/status-im/status-go/protocol/encryption/multidevice" 36 "github.com/status-im/status-go/protocol/protobuf" 37 "github.com/status-im/status-go/protocol/requests" 38 "github.com/status-im/status-go/protocol/transport" 39 "github.com/status-im/status-go/protocol/tt" 40 v1protocol "github.com/status-im/status-go/protocol/v1" 41 "github.com/status-im/status-go/server" 42 localnotifications "github.com/status-im/status-go/services/local-notifications" 43 ) 44 45 func TestMessengerCommunitiesSuite(t *testing.T) { 46 suite.Run(t, new(MessengerCommunitiesSuite)) 47 } 48 49 type MessengerCommunitiesSuite struct { 50 CommunitiesMessengerTestSuiteBase 51 owner *Messenger 52 bob *Messenger 53 alice *Messenger 54 } 55 56 func (s *MessengerCommunitiesSuite) SetupTest() { 57 s.CommunitiesMessengerTestSuiteBase.SetupTest() 58 59 s.owner = s.newMessenger("", []string{}) 60 s.owner.account.CustomizationColor = multiaccountscommon.CustomizationColorOrange 61 s.bob = s.newMessenger(bobPassword, []string{bobAccountAddress}) 62 s.bob.account.CustomizationColor = multiaccountscommon.CustomizationColorBlue 63 s.alice = s.newMessenger(alicePassword, []string{aliceAccountAddress}) 64 s.alice.account.CustomizationColor = multiaccountscommon.CustomizationColorArmy 65 66 s.owner.communitiesManager.RekeyInterval = 50 * time.Millisecond 67 68 _, err := s.owner.Start() 69 s.Require().NoError(err) 70 _, err = s.bob.Start() 71 s.Require().NoError(err) 72 _, err = s.alice.Start() 73 s.Require().NoError(err) 74 75 s.setMessengerDisplayName(s.owner, "Charlie") 76 s.setMessengerDisplayName(s.bob, "Bobby") 77 s.setMessengerDisplayName(s.alice, "Alice") 78 } 79 80 func (s *MessengerCommunitiesSuite) TearDownTest() { 81 TearDownMessenger(&s.Suite, s.owner) 82 TearDownMessenger(&s.Suite, s.bob) 83 TearDownMessenger(&s.Suite, s.alice) 84 s.CommunitiesMessengerTestSuiteBase.TearDownTest() 85 } 86 87 func (s *MessengerCommunitiesSuite) setMessengerDisplayName(m *Messenger, name string) { 88 profileKp := accounts.GetProfileKeypairForTest(true, false, false) 89 profileKp.KeyUID = m.account.KeyUID 90 profileKp.Name = DefaultProfileDisplayName 91 profileKp.Accounts[0].KeyUID = m.account.KeyUID 92 93 err := m.settings.SaveOrUpdateKeypair(profileKp) 94 s.Require().NoError(err) 95 96 err = m.SetDisplayName(name) 97 s.Require().NoError(err) 98 } 99 100 func (s *MessengerCommunitiesSuite) TestCreateCommunity() { 101 description := &requests.CreateCommunity{ 102 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 103 Name: "status", 104 Color: "#ffffff", 105 Description: "status community description", 106 } 107 response, err := s.bob.CreateCommunity(description, true) 108 109 s.Require().NoError(err) 110 s.Require().NotNil(response) 111 s.Require().Len(response.Communities(), 1) 112 s.Require().Len(response.Chats(), 1) 113 } 114 115 func (s *MessengerCommunitiesSuite) TestCreateCommunity_WithoutDefaultChannel() { 116 117 description := &requests.CreateCommunity{ 118 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 119 Name: "status", 120 Color: "#ffffff", 121 Description: "status community description", 122 } 123 response, err := s.bob.CreateCommunity(description, false) 124 125 s.Require().NoError(err) 126 s.Require().NotNil(response) 127 s.Require().Len(response.Communities(), 1) 128 s.Require().Len(response.Chats(), 0) 129 } 130 131 func (s *MessengerCommunitiesSuite) TestRetrieveCommunity() { 132 description := &requests.CreateCommunity{ 133 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 134 Name: "status", 135 Color: "#ffffff", 136 Description: "status community description", 137 } 138 139 response, err := s.bob.CreateCommunity(description, true) 140 s.Require().NoError(err) 141 s.Require().NotNil(response) 142 s.Require().Len(response.Communities(), 1) 143 s.Require().Len(response.CommunitiesSettings(), 1) 144 s.Require().Len(response.Chats(), 1) 145 146 community := response.Communities()[0] 147 communitySettings := response.CommunitiesSettings()[0] 148 149 s.Require().Equal(communitySettings.CommunityID, community.IDString()) 150 s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false) 151 152 // Send a community message 153 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 154 155 inputMessage := common.NewMessage() 156 inputMessage.ChatId = chat.ID 157 inputMessage.Text = "some text" 158 inputMessage.CommunityID = community.IDString() 159 160 err = s.bob.SaveChat(chat) 161 s.Require().NoError(err) 162 _, err = s.bob.SendChatMessage(context.Background(), inputMessage) 163 s.Require().NoError(err) 164 165 // Pull message and make sure org is received 166 err = tt.RetryWithBackOff(func() error { 167 response, err = s.alice.RetrieveAll() 168 if err != nil { 169 return err 170 } 171 if len(response.Communities()) == 0 { 172 return errors.New("community not received") 173 } 174 return nil 175 }) 176 177 s.Require().NoError(err) 178 communities, err := s.alice.Communities() 179 s.Require().NoError(err) 180 s.Require().Len(communities, 1) 181 s.Require().Len(response.Communities(), 1) 182 s.Require().Len(response.Messages(), 1) 183 s.Require().Equal(community.IDString(), response.Messages()[0].CommunityID) 184 } 185 186 func (s *MessengerCommunitiesSuite) TestJoiningOpenCommunityReturnsChatsResponse() { 187 ctx := context.Background() 188 189 openCommunityDescription := &requests.CreateCommunity{ 190 Name: "open community", 191 Description: "open community to join with no requests", 192 Color: "#26a69a", 193 HistoryArchiveSupportEnabled: true, 194 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 195 PinMessageAllMembersEnabled: false, 196 } 197 198 response, err := s.bob.CreateCommunity(openCommunityDescription, true) 199 generalChannelChatID := response.Chats()[0].ID 200 s.Require().NoError(err) 201 s.Require().NotNil(response) 202 s.Require().Len(response.Communities(), 1) 203 s.Require().Len(response.CommunitiesSettings(), 1) 204 s.Require().Len(response.Chats(), 1) 205 206 community := response.Communities()[0] 207 208 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 209 210 s.Require().NoError(s.bob.SaveChat(chat)) 211 212 message := buildTestMessage(*chat) 213 message.CommunityID = community.IDString() 214 215 // Bob sends the community link to Alice 216 response, err = s.bob.SendChatMessage(ctx, message) 217 s.Require().NoError(err) 218 s.Require().NotNil(response) 219 220 // Retrieve community link & community for Alice 221 response, err = WaitOnMessengerResponse( 222 s.alice, 223 func(r *MessengerResponse) bool { 224 return len(r.Communities()) > 0 225 }, 226 "message not received", 227 ) 228 s.Require().NoError(err) 229 s.Require().NotNil(response) 230 s.Require().Len(response.Chats(), 1) 231 232 // Alice request to join community 233 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 234 response, err = s.alice.RequestToJoinCommunity(request) 235 s.Require().NoError(err) 236 s.Require().NotNil(response) 237 s.Require().Len(response.RequestsToJoinCommunity(), 1) 238 239 requestToJoin := response.RequestsToJoinCommunity()[0] 240 s.Require().NotNil(requestToJoin) 241 s.Require().Equal(community.ID(), requestToJoin.CommunityID) 242 s.Require().NotEmpty(requestToJoin.ID) 243 s.Require().NotEmpty(requestToJoin.Clock) 244 s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 245 s.Require().Len(response.Communities(), 1) 246 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State) 247 248 // Bobs receives the request to join and it's automatically accepted 249 response, err = WaitOnMessengerResponse( 250 s.bob, 251 func(r *MessengerResponse) bool { 252 return len(r.Communities()) > 0 && len(r.RequestsToJoinCommunity()) > 0 253 }, 254 "message not received", 255 ) 256 s.Require().NoError(err) 257 s.Require().NotNil(response) 258 259 // Alice receives the updated community description with channel information 260 response, err = WaitOnMessengerResponse( 261 s.alice, 262 func(r *MessengerResponse) bool { 263 return len(r.Communities()) > 0 && len(r.chats) > 0 264 }, 265 "message not received", 266 ) 267 s.Require().NoError(err) 268 s.Require().NotNil(response) 269 270 // Check whether community's general chat is available for Alice 271 _, exists := response.chats[generalChannelChatID] 272 s.Require().True(exists) 273 } 274 275 func (s *MessengerCommunitiesSuite) TestJoinCommunity() { 276 ctx := context.Background() 277 278 description := &requests.CreateCommunity{ 279 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 280 Name: "status", 281 Color: "#ffffff", 282 Description: "status community description", 283 } 284 285 // Create an community chat 286 response, err := s.bob.CreateCommunity(description, true) 287 s.Require().NoError(err) 288 s.Require().NotNil(response) 289 s.Require().Len(response.Communities(), 1) 290 s.Require().Len(response.CommunitiesSettings(), 1) 291 292 communitySettings := response.CommunitiesSettings()[0] 293 community := response.Communities()[0] 294 295 s.Require().Equal(communitySettings.CommunityID, community.IDString()) 296 s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false) 297 298 orgChat := &protobuf.CommunityChat{ 299 Permissions: &protobuf.CommunityPermissions{ 300 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 301 }, 302 Identity: &protobuf.ChatIdentity{ 303 DisplayName: "status-core", 304 Emoji: "😎", 305 Description: "status-core community chat", 306 }, 307 HideIfPermissionsNotMet: true, 308 } 309 response, err = s.bob.CreateCommunityChat(community.ID(), orgChat) 310 s.Require().NoError(err) 311 s.Require().NotNil(response) 312 s.Require().Len(response.Communities(), 1) 313 s.Require().Len(response.Chats(), 1) 314 315 createdChat := response.Chats()[0] 316 s.Require().Equal(community.IDString(), createdChat.CommunityID) 317 s.Require().Equal(orgChat.Identity.DisplayName, createdChat.Name) 318 s.Require().Equal(orgChat.Identity.Emoji, createdChat.Emoji) 319 s.Require().NotEmpty(createdChat.ID) 320 s.Require().Equal(ChatTypeCommunityChat, createdChat.ChatType) 321 s.Require().True(createdChat.Active) 322 s.Require().NotEmpty(createdChat.Timestamp) 323 s.Require().True(strings.HasPrefix(createdChat.ID, community.IDString())) 324 s.Require().True(createdChat.HideIfPermissionsNotMet) 325 326 // Make sure the changes are reflect in the community 327 community = response.Communities()[0] 328 329 var chatIds []string 330 for k := range community.Chats() { 331 chatIds = append(chatIds, k) 332 } 333 category := &requests.CreateCommunityCategory{ 334 CommunityID: community.ID(), 335 CategoryName: "category-name", 336 ChatIDs: chatIds, 337 } 338 339 response, err = s.bob.CreateCommunityCategory(category) 340 s.Require().NoError(err) 341 s.Require().NotNil(response) 342 s.Require().Len(response.Communities(), 1) 343 s.Require().Len(response.Communities()[0].Categories(), 1) 344 345 // Make sure the changes are reflect in the community 346 community = response.Communities()[0] 347 chats := community.Chats() 348 s.Require().Len(chats, 2) 349 350 // Send a community message 351 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.bob.transport) 352 353 inputMessage := common.NewMessage() 354 inputMessage.ChatId = chat.ID 355 inputMessage.Text = "some text" 356 inputMessage.CommunityID = community.IDString() 357 358 err = s.bob.SaveChat(chat) 359 s.Require().NoError(err) 360 _, err = s.bob.SendChatMessage(context.Background(), inputMessage) 361 s.Require().NoError(err) 362 363 // Pull message and make sure org is received 364 err = tt.RetryWithBackOff(func() error { 365 response, err = s.alice.RetrieveAll() 366 if err != nil { 367 return err 368 } 369 if len(response.Communities()) == 0 { 370 return errors.New("community not received") 371 } 372 return nil 373 }) 374 375 s.Require().NoError(err) 376 communities, err := s.alice.Communities() 377 s.Require().NoError(err) 378 s.Require().Len(communities, 1) 379 s.Require().Len(response.Communities(), 1) 380 s.Require().Len(response.Messages(), 1) 381 s.Require().Equal(community.IDString(), response.Messages()[0].CommunityID) 382 383 // We join the org 384 response, err = s.alice.JoinCommunity(ctx, community.ID(), false) 385 s.Require().NoError(err) 386 s.Require().NotNil(response) 387 s.Require().Len(response.Communities(), 1) 388 s.Require().True(response.Communities()[0].Joined()) 389 s.Require().True(response.Communities()[0].JoinedAt() > 0) 390 s.Require().Len(response.Chats(), 2) 391 s.Require().Len(response.Communities()[0].Categories(), 1) 392 s.Require().Len(response.notifications, 1) 393 for _, notification := range response.notifications { 394 s.Require().Equal(notification.Title, community.Name()) 395 s.Require().EqualValues(notification.BodyType, localnotifications.CategoryCommunityJoined) 396 s.Require().EqualValues(notification.Category, localnotifications.CategoryCommunityJoined) 397 } 398 399 var categoryID string 400 for k := range response.Communities()[0].Categories() { 401 categoryID = k 402 } 403 404 // The chat should be created 405 406 found := false 407 for _, createdChat := range response.Chats() { 408 if orgChat.Identity.DisplayName == createdChat.Name { 409 found = true 410 s.Require().Equal(community.IDString(), createdChat.CommunityID) 411 s.Require().Equal(orgChat.Identity.DisplayName, createdChat.Name) 412 s.Require().Equal(orgChat.Identity.Emoji, createdChat.Emoji) 413 s.Require().NotEmpty(createdChat.ID) 414 s.Require().Equal(ChatTypeCommunityChat, createdChat.ChatType) 415 s.Require().Equal(categoryID, createdChat.CategoryID) 416 s.Require().True(createdChat.Active) 417 s.Require().NotEmpty(createdChat.Timestamp) 418 s.Require().True(strings.HasPrefix(createdChat.ID, community.IDString())) 419 } 420 } 421 s.Require().True(found) 422 423 // Create another org chat 424 orgChat = &protobuf.CommunityChat{ 425 Permissions: &protobuf.CommunityPermissions{ 426 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 427 }, 428 Identity: &protobuf.ChatIdentity{ 429 DisplayName: "status-core-ui", 430 Emoji: "👍", 431 Description: "status-core-ui community chat", 432 }, 433 } 434 response, err = s.bob.CreateCommunityChat(community.ID(), orgChat) 435 s.Require().NoError(err) 436 s.Require().NotNil(response) 437 s.Require().Len(response.Communities(), 1) 438 s.Require().Len(response.Chats(), 1) 439 440 var actualChat *Chat 441 // Pull message, this time it should be received as advertised automatically 442 err = tt.RetryWithBackOff(func() error { 443 response, err = s.alice.RetrieveAll() 444 if err != nil { 445 return err 446 } 447 if len(response.Communities()) != 1 { 448 return errors.New("community not received") 449 } 450 451 for _, c := range response.Chats() { 452 if c.Name == orgChat.Identity.DisplayName { 453 actualChat = c 454 return nil 455 } 456 } 457 return errors.New("chat not found") 458 }) 459 460 s.Require().NoError(err) 461 communities, err = s.alice.Communities() 462 s.Require().NoError(err) 463 s.Require().Len(communities, 1) 464 s.Require().Len(response.Communities(), 1) 465 s.Require().NotNil(actualChat) 466 s.Require().Equal(community.IDString(), actualChat.CommunityID) 467 s.Require().Equal(orgChat.Identity.DisplayName, actualChat.Name) 468 s.Require().Equal(orgChat.Identity.Emoji, actualChat.Emoji) 469 s.Require().NotEmpty(actualChat.ID) 470 s.Require().Equal(ChatTypeCommunityChat, actualChat.ChatType) 471 s.Require().True(actualChat.Active) 472 s.Require().NotEmpty(actualChat.Timestamp) 473 s.Require().True(strings.HasPrefix(actualChat.ID, community.IDString())) 474 475 // We leave the org 476 response, err = s.alice.LeaveCommunity(community.ID()) 477 s.Require().NoError(err) 478 s.Require().NotNil(response) 479 s.Require().Len(response.Communities(), 1) 480 s.Require().False(response.Communities()[0].Joined()) 481 s.Require().Len(response.RemovedChats(), 3) 482 } 483 484 func (s *MessengerCommunitiesSuite) createCommunity() (*communities.Community, *Chat) { 485 return createCommunity(&s.Suite, s.owner) 486 } 487 488 func (s *MessengerCommunitiesSuite) advertiseCommunityTo(community *communities.Community, owner *Messenger, user *Messenger) { 489 advertiseCommunityTo(&s.Suite, community, owner, user) 490 } 491 492 func (s *MessengerCommunitiesSuite) TestCommunityContactCodeAdvertisement() { 493 // add bob's profile keypair 494 bobProfileKp := accounts.GetProfileKeypairForTest(true, false, false) 495 bobProfileKp.KeyUID = s.bob.account.KeyUID 496 bobProfileKp.Accounts[0].KeyUID = s.bob.account.KeyUID 497 498 err := s.bob.settings.SaveOrUpdateKeypair(bobProfileKp) 499 s.Require().NoError(err) 500 501 // create community and make bob and alice join to it 502 community, _ := s.createCommunity() 503 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.bob) 504 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice) 505 506 s.joinCommunity(community, s.owner, s.bob) 507 s.joinCommunity(community, s.owner, s.alice) 508 509 // Trigger ContactCodeAdvertisement 510 err = s.bob.SetDisplayName("bobby") 511 s.Require().NoError(err) 512 err = s.bob.SetBio("I like P2P chats") 513 s.Require().NoError(err) 514 515 // Ensure alice receives bob's ContactCodeAdvertisement 516 err = tt.RetryWithBackOff(func() error { 517 response, err := s.alice.RetrieveAll() 518 if err != nil { 519 return err 520 } 521 if len(response.Contacts) == 0 { 522 return errors.New("no contacts in response") 523 } 524 if response.Contacts[0].DisplayName != "bobby" { 525 return errors.New("display name was not updated") 526 } 527 if response.Contacts[0].Bio != "I like P2P chats" { 528 return errors.New("bio was not updated") 529 } 530 return nil 531 }) 532 s.Require().NoError(err) 533 } 534 535 func (s *MessengerCommunitiesSuite) TestPostToCommunityChat() { 536 community, chat := s.createCommunity() 537 538 chatID := chat.ID 539 inputMessage := common.NewMessage() 540 inputMessage.ChatId = chatID 541 inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN 542 inputMessage.Text = "some text" 543 544 ctx := context.Background() 545 546 s.advertiseCommunityTo(community, s.owner, s.alice) 547 548 // Send message without even spectating fails 549 _, err := s.alice.SendChatMessage(ctx, inputMessage) 550 s.Require().Error(err) 551 552 // Sending a message without joining fails 553 _, err = s.alice.SpectateCommunity(community.ID()) 554 s.Require().NoError(err) 555 _, err = s.alice.SendChatMessage(ctx, inputMessage) 556 s.Require().Error(err) 557 558 // Sending should work now 559 s.joinCommunity(community, s.owner, s.alice) 560 _, err = s.alice.SendChatMessage(ctx, inputMessage) 561 s.Require().NoError(err) 562 563 var response *MessengerResponse 564 // Pull message and make sure org is received 565 err = tt.RetryWithBackOff(func() error { 566 response, err = s.owner.RetrieveAll() 567 if err != nil { 568 return err 569 } 570 if len(response.messages) == 0 { 571 return errors.New("message not received") 572 } 573 return nil 574 }) 575 576 s.Require().NoError(err) 577 s.Require().Len(response.Messages(), 1) 578 s.Require().Equal(inputMessage.Text, response.Messages()[0].Text) 579 s.Require().Equal(s.alice.account.GetCustomizationColor(), response.Contacts[0].CustomizationColor) 580 581 // check if response contains the chat we're interested in 582 // we use this instead of checking just the length of the chat because 583 // a CommunityDescription message might be received in the meantime due to syncing 584 // hence response.Chats() might contain the general chat, and the new chat; 585 // or only the new chat if the CommunityDescription message has not arrived 586 found := false 587 for _, chat := range response.Chats() { 588 if chat.ID == chatID { 589 found = true 590 } 591 } 592 s.Require().True(found) 593 } 594 595 func (s *MessengerCommunitiesSuite) TestPinMessageInCommunityChat() { 596 ctx := context.Background() 597 598 // Create a community 599 description := &requests.CreateCommunity{ 600 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 601 Name: "status", 602 Color: "#ffffff", 603 Description: "status community description", 604 PinMessageAllMembersEnabled: true, 605 } 606 607 response, err := s.owner.CreateCommunity(description, true) 608 s.Require().NoError(err) 609 s.Require().NotNil(response) 610 s.Require().Len(response.Communities(), 1) 611 612 community := response.Communities()[0] 613 s.Require().NotNil(community) 614 s.Require().Equal(community.AllowsAllMembersToPinMessage(), true) 615 616 // Create a community chat 617 orgChat := &protobuf.CommunityChat{ 618 Permissions: &protobuf.CommunityPermissions{ 619 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 620 }, 621 Identity: &protobuf.ChatIdentity{ 622 DisplayName: "status-core", 623 Emoji: "😎", 624 Description: "status-core community chat", 625 }, 626 } 627 response, err = s.owner.CreateCommunityChat(community.ID(), orgChat) 628 s.Require().NoError(err) 629 s.Require().NotNil(response) 630 s.Require().Len(response.Communities(), 1) 631 s.Require().Len(response.Chats(), 1) 632 chat := response.Chats()[0] 633 s.Require().NotNil(chat) 634 635 s.advertiseCommunityTo(community, s.owner, s.bob) 636 s.joinCommunity(community, s.owner, s.bob) 637 638 inputMessage := common.NewMessage() 639 inputMessage.ChatId = chat.ID 640 inputMessage.ContentType = protobuf.ChatMessage_TEXT_PLAIN 641 inputMessage.Text = "message to be pinned" 642 643 sendResponse, err := s.bob.SendChatMessage(ctx, inputMessage) 644 s.Require().NoError(err) 645 s.Require().Len(sendResponse.Messages(), 1) 646 647 // bob should be able to pin the message 648 pinMessage := common.NewPinMessage() 649 pinMessage.ChatId = chat.ID 650 pinMessage.MessageId = inputMessage.ID 651 pinMessage.Pinned = true 652 sendResponse, err = s.bob.SendPinMessage(ctx, pinMessage) 653 s.Require().NoError(err) 654 s.Require().Len(sendResponse.PinMessages(), 1) 655 656 // alice does not fully join the community, 657 // so she should not be able to send the pin message 658 s.advertiseCommunityTo(community, s.owner, s.alice) 659 response, err = s.alice.SpectateCommunity(community.ID()) 660 s.Require().NotNil(response) 661 s.Require().NoError(err) 662 failedPinMessage := common.NewPinMessage() 663 failedPinMessage.ChatId = chat.ID 664 failedPinMessage.MessageId = inputMessage.ID 665 failedPinMessage.Pinned = true 666 sendResponse, err = s.alice.SendPinMessage(ctx, failedPinMessage) 667 s.Require().Nil(sendResponse) 668 s.Require().Error(err, "can't pin message") 669 } 670 671 func (s *MessengerCommunitiesSuite) TestImportCommunity() { 672 ctx := context.Background() 673 674 community, _ := s.createCommunity() 675 676 category := &requests.CreateCommunityCategory{ 677 CommunityID: community.ID(), 678 CategoryName: "category-name", 679 ChatIDs: []string{}, 680 } 681 682 response, err := s.owner.CreateCommunityCategory(category) 683 s.Require().NoError(err) 684 community = response.Communities()[0] 685 686 s.advertiseCommunityTo(community, s.owner, s.bob) 687 s.joinCommunity(community, s.owner, s.bob) 688 689 privateKey, err := s.owner.ExportCommunity(community.ID()) 690 s.Require().NoError(err) 691 692 _, err = s.alice.ImportCommunity(ctx, privateKey) 693 s.Require().NoError(err) 694 695 newDescription := "new description set post import" 696 _, err = s.alice.EditCommunity(&requests.EditCommunity{ 697 CommunityID: community.ID(), 698 CreateCommunity: requests.CreateCommunity{ 699 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 700 Name: community.Name(), 701 Color: community.Color(), 702 Description: newDescription, 703 }, 704 }) 705 s.Require().NoError(err) 706 707 // bob receives new description 708 _, err = WaitOnMessengerResponse(s.bob, func(r *MessengerResponse) bool { 709 return len(r.Communities()) > 0 && r.Communities()[0].DescriptionText() == newDescription 710 }, "new description not received") 711 s.Require().NoError(err) 712 } 713 714 func (s *MessengerCommunitiesSuite) TestRemovePrivateKey() { 715 description := &requests.CreateCommunity{ 716 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 717 Name: "status", 718 Color: "#ffffff", 719 Description: "status community description", 720 } 721 722 // Create an community chat 723 response, err := s.bob.CreateCommunity(description, true) 724 s.Require().NoError(err) 725 s.Require().NotNil(response) 726 s.Require().Len(response.Communities(), 1) 727 728 community := response.Communities()[0] 729 s.Require().True(community.IsControlNode()) 730 s.Require().True(community.IsControlNode()) 731 732 response, err = s.bob.RemovePrivateKey(community.ID()) 733 s.Require().NoError(err) 734 s.Require().Len(response.Communities(), 1) 735 736 community = response.Communities()[0] 737 s.Require().True(community.IsOwner()) 738 s.Require().False(community.IsControlNode()) 739 } 740 741 func (s *MessengerCommunitiesSuite) TestRolesAfterImportCommunity() { 742 ctx := context.Background() 743 744 description := &requests.CreateCommunity{ 745 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 746 Name: "status", 747 Color: "#ffffff", 748 Description: "status community description", 749 } 750 751 // Create a community chat 752 response, err := s.bob.CreateCommunity(description, true) 753 s.Require().NoError(err) 754 s.Require().NotNil(response) 755 s.Require().Len(response.Communities(), 1) 756 s.Require().Len(response.CommunitiesSettings(), 1) 757 s.Require().True(response.Communities()[0].Joined()) 758 s.Require().True(response.Communities()[0].IsControlNode()) 759 s.Require().True(response.Communities()[0].IsMemberOwner(&s.bob.identity.PublicKey)) 760 s.Require().False(response.Communities()[0].IsMemberOwner(&s.alice.identity.PublicKey)) 761 762 community := response.Communities()[0] 763 communitySettings := response.CommunitiesSettings()[0] 764 765 s.Require().Equal(communitySettings.CommunityID, community.IDString()) 766 s.Require().Equal(communitySettings.HistoryArchiveSupportEnabled, false) 767 768 category := &requests.CreateCommunityCategory{ 769 CommunityID: community.ID(), 770 CategoryName: "category-name", 771 ChatIDs: []string{}, 772 } 773 774 response, err = s.bob.CreateCommunityCategory(category) 775 s.Require().NoError(err) 776 community = response.Communities()[0] 777 778 privateKey, err := s.bob.ExportCommunity(community.ID()) 779 s.Require().NoError(err) 780 781 response, err = s.alice.ImportCommunity(ctx, privateKey) 782 s.Require().NoError(err) 783 s.Require().True(response.Communities()[0].IsMemberOwner(&s.alice.identity.PublicKey)) 784 } 785 786 func (s *MessengerCommunitiesSuite) TestRequestAccess() { 787 ctx := context.Background() 788 789 description := &requests.CreateCommunity{ 790 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 791 Name: "status", 792 Color: "#ffffff", 793 Description: "status community description", 794 } 795 796 // Create an community chat 797 response, err := s.bob.CreateCommunity(description, true) 798 s.Require().NoError(err) 799 s.Require().NotNil(response) 800 s.Require().Len(response.Communities(), 1) 801 802 community := response.Communities()[0] 803 804 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 805 806 s.Require().NoError(s.bob.SaveChat(chat)) 807 808 message := buildTestMessage(*chat) 809 message.CommunityID = community.IDString() 810 811 // We send a community link to alice 812 response, err = s.bob.SendChatMessage(ctx, message) 813 s.Require().NoError(err) 814 s.Require().NotNil(response) 815 816 // Retrieve community link & community 817 err = tt.RetryWithBackOff(func() error { 818 response, err = s.alice.RetrieveAll() 819 if err != nil { 820 return err 821 } 822 if len(response.Communities()) == 0 { 823 return errors.New("message not received") 824 } 825 return nil 826 }) 827 828 s.Require().NoError(err) 829 830 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 831 // We try to join the org 832 response, err = s.alice.RequestToJoinCommunity(request) 833 s.Require().NoError(err) 834 s.Require().NotNil(response) 835 s.Require().Len(response.RequestsToJoinCommunity(), 1) 836 837 s.Require().Len(response.ActivityCenterNotifications(), 1) 838 839 notification := response.ActivityCenterNotifications()[0] 840 s.Require().NotNil(notification) 841 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 842 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 843 s.Require().Equal(notification.Read, true) 844 s.Require().Equal(notification.Accepted, false) 845 s.Require().Equal(notification.Dismissed, false) 846 847 requestToJoin1 := response.RequestsToJoinCommunity()[0] 848 s.Require().NotNil(requestToJoin1) 849 s.Require().Equal(community.ID(), requestToJoin1.CommunityID) 850 s.Require().True(requestToJoin1.Our) 851 s.Require().NotEmpty(requestToJoin1.ID) 852 s.Require().NotEmpty(requestToJoin1.Clock) 853 s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 854 s.Require().Equal(requestToJoin1.CustomizationColor, s.alice.account.GetCustomizationColor()) 855 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State) 856 857 // Make sure clock is not empty 858 s.Require().NotEmpty(requestToJoin1.Clock) 859 860 s.Require().Len(response.Communities(), 1) 861 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock) 862 863 // pull all communities to make sure we set RequestedToJoinAt 864 865 allCommunities, err := s.alice.Communities() 866 s.Require().NoError(err) 867 s.Require().Len(allCommunities, 1) 868 s.Require().Equal(allCommunities[0].ID(), community.ID()) 869 s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock) 870 871 // pull to make sure it has been saved 872 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 873 s.Require().NoError(err) 874 s.Require().Len(requestsToJoin, 1) 875 876 // Make sure the requests are fetched also by community 877 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 878 s.Require().NoError(err) 879 s.Require().Len(requestsToJoin, 1) 880 881 // Retrieve request to join 882 err = tt.RetryWithBackOff(func() error { 883 response, err = s.bob.RetrieveAll() 884 if err != nil { 885 return err 886 } 887 if len(response.RequestsToJoinCommunity()) == 0 { 888 return errors.New("request to join community not received") 889 } 890 return nil 891 }) 892 s.Require().NoError(err) 893 s.Require().Len(response.RequestsToJoinCommunity(), 1) 894 895 requestToJoin2 := response.RequestsToJoinCommunity()[0] 896 897 s.Require().NotNil(requestToJoin2) 898 s.Require().Equal(community.ID(), requestToJoin2.CommunityID) 899 s.Require().False(requestToJoin2.Our) 900 s.Require().NotEmpty(requestToJoin2.ID) 901 s.Require().NotEmpty(requestToJoin2.Clock) 902 s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 903 s.Require().Equal(requestToJoin2.CustomizationColor, s.alice.account.GetCustomizationColor()) 904 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State) 905 906 s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID) 907 908 s.Require().Len(response.ActivityCenterNotifications(), 1) 909 910 notification = response.ActivityCenterNotifications()[0] 911 s.Require().NotNil(notification) 912 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 913 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 914 s.Require().Equal(notification.Read, false) 915 s.Require().Equal(notification.Accepted, false) 916 s.Require().Equal(notification.Dismissed, false) 917 918 // Accept request 919 920 acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin1.ID} 921 922 response, err = s.bob.AcceptRequestToJoinCommunity(acceptRequestToJoin) 923 s.Require().NoError(err) 924 s.Require().NotNil(response) 925 926 s.Require().Len(response.Communities(), 1) 927 928 updatedCommunity := response.Communities()[0] 929 930 s.Require().NotNil(updatedCommunity) 931 s.Require().True(updatedCommunity.HasMember(&s.alice.identity.PublicKey)) 932 933 s.Require().Len(response.ActivityCenterNotifications(), 1) 934 935 notification = response.ActivityCenterNotifications()[0] 936 s.Require().NotNil(notification) 937 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 938 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted) 939 s.Require().Equal(notification.Read, true) 940 s.Require().Equal(notification.Accepted, true) 941 s.Require().Equal(notification.Dismissed, false) 942 943 // Pull message and make sure org is received 944 err = tt.RetryWithBackOff(func() error { 945 response, err = s.alice.RetrieveAll() 946 if err != nil { 947 return err 948 } 949 if len(response.Communities()) == 0 { 950 return errors.New("community not received") 951 } 952 return nil 953 }) 954 955 s.Require().NoError(err) 956 s.Require().NotNil(response) 957 958 s.Require().Len(response.RequestsToJoinCommunity(), 1) 959 s.Require().Equal(communities.RequestToJoinStateAccepted, response.RequestsToJoinCommunity()[0].State) 960 961 s.Require().Len(response.ActivityCenterNotifications(), 1) 962 963 notification = response.ActivityCenterNotifications()[0] 964 s.Require().NotNil(notification) 965 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 966 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted) 967 s.Require().Equal(notification.Read, false) 968 s.Require().Equal(notification.Accepted, false) 969 s.Require().Equal(notification.Dismissed, false) 970 971 s.Require().Len(response.Communities(), 1) 972 aliceCommunity := response.Communities()[0] 973 974 s.Require().Equal(community.ID(), aliceCommunity.ID()) 975 s.Require().True(aliceCommunity.HasMember(&s.alice.identity.PublicKey)) 976 977 // Community should be joined at this point 978 s.Require().True(aliceCommunity.Joined()) 979 980 // Make sure the requests are not pending on either sides 981 requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID()) 982 s.Require().NoError(err) 983 s.Require().Len(requestsToJoin, 0) 984 985 requestsToJoin, err = s.alice.MyPendingRequestsToJoin() 986 s.Require().NoError(err) 987 s.Require().Len(requestsToJoin, 0) 988 989 } 990 991 func (s *MessengerCommunitiesSuite) TestDeletePendingRequestAccess() { 992 ctx := context.Background() 993 994 description := &requests.CreateCommunity{ 995 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 996 Name: "status", 997 Color: "#ffffff", 998 Description: "status community description", 999 } 1000 1001 // Bob creates a community 1002 response, err := s.bob.CreateCommunity(description, true) 1003 s.Require().NoError(err) 1004 s.Require().NotNil(response) 1005 s.Require().Len(response.Communities(), 1) 1006 1007 community := response.Communities()[0] 1008 1009 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 1010 1011 s.Require().NoError(s.bob.SaveChat(chat)) 1012 1013 message := buildTestMessage(*chat) 1014 message.CommunityID = community.IDString() 1015 1016 // Bob sends the community link to Alice 1017 response, err = s.bob.SendChatMessage(ctx, message) 1018 s.Require().NoError(err) 1019 s.Require().NotNil(response) 1020 1021 // Retrieve community link & community for Alice 1022 err = tt.RetryWithBackOff(func() error { 1023 response, err = s.alice.RetrieveAll() 1024 if err != nil { 1025 return err 1026 } 1027 if len(response.Communities()) == 0 { 1028 return errors.New("message not received") 1029 } 1030 return nil 1031 }) 1032 1033 s.Require().NoError(err) 1034 1035 // Alice request to join community 1036 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 1037 response, err = s.alice.RequestToJoinCommunity(request) 1038 s.Require().NoError(err) 1039 s.Require().NotNil(response) 1040 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1041 1042 requestToJoin := response.RequestsToJoinCommunity()[0] 1043 s.Require().NotNil(requestToJoin) 1044 s.Require().Equal(community.ID(), requestToJoin.CommunityID) 1045 s.Require().NotEmpty(requestToJoin.ID) 1046 s.Require().NotEmpty(requestToJoin.Clock) 1047 s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1048 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State) 1049 1050 s.Require().Len(response.Communities(), 1) 1051 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin.Clock) 1052 1053 // updating request clock by 8 days back 1054 requestTime := uint64(time.Now().AddDate(0, 0, -8).Unix()) 1055 err = s.alice.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime) 1056 s.Require().NoError(err) 1057 1058 // pull to make sure it has been saved 1059 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 1060 s.Require().NoError(err) 1061 s.Require().Len(requestsToJoin, 1) 1062 1063 requestToJoin = requestsToJoin[0] 1064 s.Require().Equal(requestToJoin.Clock, requestTime) 1065 1066 // Make sure the requests are fetched also by community 1067 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 1068 s.Require().NoError(err) 1069 s.Require().Len(requestsToJoin, 1) 1070 1071 // Retrieve request to join 1072 bobRetrieveAll := func() (*MessengerResponse, error) { 1073 return s.bob.RetrieveAll() 1074 } 1075 err = tt.RetryWithBackOff(func() error { 1076 response, err = bobRetrieveAll() 1077 if err != nil { 1078 return err 1079 } 1080 1081 if len(response.RequestsToJoinCommunity()) == 0 { 1082 return errors.New("request to join community not received") 1083 } 1084 1085 // updating request clock by 8 days back 1086 requestToJoin := response.RequestsToJoinCommunity()[0] 1087 err = s.bob.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime) 1088 if err != nil { 1089 return err 1090 } 1091 1092 if len(response.ActivityCenterNotifications()) == 0 { 1093 return errors.New("request to join community notification not added in activity center") 1094 } 1095 return nil 1096 }) 1097 s.Require().NoError(err) 1098 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1099 1100 // Check activity center notification for Bob 1101 fetchActivityCenterNotificationsForAdmin := func() (*ActivityCenterPaginationResponse, error) { 1102 return s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 1103 Cursor: "", 1104 Limit: 10, 1105 ActivityTypes: []ActivityCenterType{}, 1106 ReadType: ActivityCenterQueryParamsReadUnread, 1107 }) 1108 } 1109 notifications, err := fetchActivityCenterNotificationsForAdmin() 1110 s.Require().NoError(err) 1111 s.Require().Len(notifications.Notifications, 1) 1112 1113 notification := notifications.Notifications[0] 1114 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1115 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1116 1117 // Delete pending request to join 1118 response, err = s.alice.CheckAndDeletePendingRequestToJoinCommunity(ctx, true) 1119 s.Require().NoError(err) 1120 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1121 s.Require().Len(response.ActivityCenterNotifications(), 1) 1122 1123 requestToJoin = response.RequestsToJoinCommunity()[0] 1124 s.Require().True(requestToJoin.Deleted) 1125 1126 notification = response.ActivityCenterNotifications()[0] 1127 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 1128 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusIdle) 1129 1130 response, err = s.bob.CheckAndDeletePendingRequestToJoinCommunity(ctx, true) 1131 s.Require().NoError(err) 1132 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1133 s.Require().Len(response.ActivityCenterNotifications(), 1) 1134 1135 requestToJoin = response.RequestsToJoinCommunity()[0] 1136 s.Require().True(requestToJoin.Deleted) 1137 1138 notification = response.ActivityCenterNotifications()[0] 1139 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1140 s.Require().True(notification.Deleted) 1141 1142 // Alice request to join community 1143 request = s.createRequestToJoinCommunity(community.ID(), s.alice) 1144 response, err = s.alice.RequestToJoinCommunity(request) 1145 s.Require().NoError(err) 1146 s.Require().NotNil(response) 1147 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1148 1149 aliceRequestToJoin := response.RequestsToJoinCommunity()[0] 1150 1151 // Retrieve request to join and Check activity center notification for Bob 1152 err = tt.RetryWithBackOff(func() error { 1153 response, err = bobRetrieveAll() 1154 if err != nil { 1155 return err 1156 } 1157 // NOTE: we might receive multiple requests to join in case of re-transmissions 1158 // because request to join are hard deleted from the database, we can't check 1159 // whether that's an old one or a new one. So here we test for the specific id 1160 1161 for _, r := range response.RequestsToJoinCommunity() { 1162 if bytes.Equal(r.ID, aliceRequestToJoin.ID) { 1163 return nil 1164 } 1165 } 1166 return errors.New("request to join not found") 1167 }) 1168 s.Require().NoError(err) 1169 1170 // Check activity center notification for Bob 1171 notifications, err = fetchActivityCenterNotificationsForAdmin() 1172 1173 s.Require().NoError(err) 1174 s.Require().Len(notifications.Notifications, 1) 1175 1176 notification = notifications.Notifications[0] 1177 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1178 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1179 1180 } 1181 1182 func (s *MessengerCommunitiesSuite) TestDeletePendingRequestAccessWithDeclinedState() { 1183 ctx := context.Background() 1184 1185 description := &requests.CreateCommunity{ 1186 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 1187 Name: "status", 1188 Color: "#ffffff", 1189 Description: "status community description", 1190 } 1191 1192 // Bob creates a community 1193 response, err := s.bob.CreateCommunity(description, true) 1194 s.Require().NoError(err) 1195 s.Require().NotNil(response) 1196 s.Require().Len(response.Communities(), 1) 1197 1198 community := response.Communities()[0] 1199 1200 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 1201 1202 s.Require().NoError(s.bob.SaveChat(chat)) 1203 1204 message := buildTestMessage(*chat) 1205 message.CommunityID = community.IDString() 1206 1207 // Bob sends the community link to Alice 1208 response, err = s.bob.SendChatMessage(ctx, message) 1209 s.Require().NoError(err) 1210 s.Require().NotNil(response) 1211 1212 // Retrieve community link & community for Alice 1213 err = tt.RetryWithBackOff(func() error { 1214 response, err = s.alice.RetrieveAll() 1215 if err != nil { 1216 return err 1217 } 1218 if len(response.Communities()) == 0 { 1219 return errors.New("message not received") 1220 } 1221 return nil 1222 }) 1223 1224 s.Require().NoError(err) 1225 1226 // Alice request to join community 1227 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 1228 response, err = s.alice.RequestToJoinCommunity(request) 1229 s.Require().NoError(err) 1230 s.Require().NotNil(response) 1231 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1232 1233 notification := response.ActivityCenterNotifications()[0] 1234 s.Require().NotNil(notification) 1235 s.Require().NotEmpty(notification.ID) 1236 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 1237 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1238 s.Require().Equal(notification.Deleted, false) 1239 s.Require().Equal(notification.Read, true) 1240 1241 requestToJoin := response.RequestsToJoinCommunity()[0] 1242 s.Require().NotNil(requestToJoin) 1243 s.Require().Equal(community.ID(), requestToJoin.CommunityID) 1244 s.Require().NotEmpty(requestToJoin.ID) 1245 s.Require().NotEmpty(requestToJoin.Clock) 1246 s.Require().Equal(requestToJoin.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1247 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin.State) 1248 1249 s.Require().Len(response.Communities(), 1) 1250 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin.Clock) 1251 1252 // Alice deletes activity center notification 1253 var updatedAt uint64 = 99 1254 _, err = s.alice.MarkActivityCenterNotificationsDeleted(ctx, []types.HexBytes{notification.ID}, updatedAt, true) 1255 s.Require().NoError(err) 1256 1257 // Check activity center notification for Bob after deleting 1258 notifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 1259 Cursor: "", 1260 Limit: 10, 1261 ActivityTypes: []ActivityCenterType{}, 1262 ReadType: ActivityCenterQueryParamsReadUnread, 1263 }) 1264 s.Require().NoError(err) 1265 s.Require().Len(notifications.Notifications, 0) 1266 1267 // updating request clock by 8 days back 1268 requestTime := uint64(time.Now().AddDate(0, 0, -8).Unix()) 1269 err = s.alice.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime) 1270 s.Require().NoError(err) 1271 1272 // pull to make sure it has been saved 1273 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 1274 s.Require().NoError(err) 1275 s.Require().Len(requestsToJoin, 1) 1276 1277 requestToJoin = requestsToJoin[0] 1278 s.Require().Equal(requestToJoin.Clock, requestTime) 1279 1280 // Make sure the requests are fetched also by community 1281 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 1282 s.Require().NoError(err) 1283 s.Require().Len(requestsToJoin, 1) 1284 1285 bobRetrieveAll := func() (*MessengerResponse, error) { 1286 return s.bob.RetrieveAll() 1287 } 1288 1289 // Retrieve request to join 1290 err = tt.RetryWithBackOff(func() error { 1291 response, err = bobRetrieveAll() 1292 if err != nil { 1293 return err 1294 } 1295 1296 if len(response.RequestsToJoinCommunity()) == 0 { 1297 return errors.New("request to join community not received") 1298 } 1299 1300 // updating request clock by 8 days back 1301 requestToJoin := response.RequestsToJoinCommunity()[0] 1302 err = s.bob.communitiesManager.UpdateClockInRequestToJoin(requestToJoin.ID, requestTime) 1303 if err != nil { 1304 return err 1305 } 1306 1307 if len(response.ActivityCenterNotifications()) == 0 { 1308 return errors.New("request to join community notification not added in activity center") 1309 } 1310 return nil 1311 }) 1312 s.Require().NoError(err) 1313 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1314 1315 // Check activity center notification for Bob 1316 fetchActivityCenterNotificationsForAdmin := func() (*ActivityCenterPaginationResponse, error) { 1317 return s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 1318 Cursor: "", 1319 Limit: 10, 1320 ActivityTypes: []ActivityCenterType{}, 1321 ReadType: ActivityCenterQueryParamsReadUnread, 1322 }) 1323 } 1324 1325 notifications, err = fetchActivityCenterNotificationsForAdmin() 1326 s.Require().NoError(err) 1327 s.Require().Len(notifications.Notifications, 1) 1328 1329 notification = notifications.Notifications[0] 1330 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1331 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1332 1333 // Check if admin sees requests correctly 1334 requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID()) 1335 s.Require().NoError(err) 1336 s.Require().Len(requestsToJoin, 1) 1337 1338 requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID()) 1339 s.Require().NoError(err) 1340 s.Require().Len(requestsToJoin, 0) 1341 1342 // Decline request 1343 declinedRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: requestToJoin.ID} 1344 response, err = s.bob.DeclineRequestToJoinCommunity(declinedRequestToJoin) 1345 s.Require().NoError(err) 1346 s.Require().NotNil(response) 1347 s.Require().Len(response.ActivityCenterNotifications(), 1) 1348 1349 notification = response.ActivityCenterNotifications()[0] 1350 s.Require().NotNil(notification) 1351 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1352 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusDeclined) 1353 s.Require().Equal(notification.Read, true) 1354 s.Require().Equal(notification.Accepted, false) 1355 s.Require().Equal(notification.Dismissed, true) 1356 1357 // Check if admin sees requests correctly 1358 requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID()) 1359 s.Require().NoError(err) 1360 s.Require().Len(requestsToJoin, 0) 1361 1362 requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID()) 1363 s.Require().NoError(err) 1364 s.Require().Len(requestsToJoin, 1) 1365 1366 // Bob deletes activity center notification 1367 updatedAt++ 1368 _, err = s.bob.MarkActivityCenterNotificationsDeleted(ctx, []types.HexBytes{notification.ID}, updatedAt, true) 1369 s.Require().NoError(err) 1370 1371 // Check activity center notification for Bob after deleting 1372 notifications, err = fetchActivityCenterNotificationsForAdmin() 1373 s.Require().NoError(err) 1374 s.Require().Len(notifications.Notifications, 0) 1375 1376 // Delete pending request to join 1377 response, err = s.alice.CheckAndDeletePendingRequestToJoinCommunity(ctx, true) 1378 s.Require().NoError(err) 1379 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1380 1381 requestToJoin = response.RequestsToJoinCommunity()[0] 1382 s.Require().True(requestToJoin.Deleted) 1383 1384 notification = response.ActivityCenterNotifications()[0] 1385 s.Require().NotNil(notification) 1386 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 1387 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusIdle) 1388 s.Require().Equal(notification.Read, false) 1389 s.Require().Equal(notification.Deleted, false) 1390 1391 notificationState := response.ActivityCenterState() 1392 s.Require().False(notificationState.HasSeen) 1393 1394 // Alice request to join community 1395 request = s.createRequestToJoinCommunity(community.ID(), s.alice) 1396 response, err = s.alice.RequestToJoinCommunity(request) 1397 s.Require().NoError(err) 1398 s.Require().NotNil(response) 1399 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1400 1401 // Retrieve request to join and Check activity center notification for Bob 1402 err = tt.RetryWithBackOff(func() error { 1403 response, err = bobRetrieveAll() 1404 if err != nil { 1405 return err 1406 } 1407 1408 if len(response.RequestsToJoinCommunity()) == 0 { 1409 return errors.New("request to join community not received") 1410 } 1411 1412 if len(response.ActivityCenterNotifications()) == 0 { 1413 return errors.New("request to join community notification not added in activity center") 1414 } 1415 1416 return nil 1417 }) 1418 s.Require().NoError(err) 1419 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1420 1421 // Check activity center notification for Bob 1422 notifications, err = fetchActivityCenterNotificationsForAdmin() 1423 1424 s.Require().NoError(err) 1425 s.Require().Len(notifications.Notifications, 1) 1426 1427 notification = notifications.Notifications[0] 1428 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1429 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1430 s.Require().False(notification.Deleted) 1431 1432 } 1433 1434 func (s *MessengerCommunitiesSuite) TestCancelRequestAccess() { 1435 ctx := context.Background() 1436 1437 description := &requests.CreateCommunity{ 1438 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 1439 Name: "status", 1440 Color: "#ffffff", 1441 Description: "status community description", 1442 } 1443 1444 // Create an community chat 1445 response, err := s.bob.CreateCommunity(description, true) 1446 s.Require().NoError(err) 1447 s.Require().NotNil(response) 1448 s.Require().Len(response.Communities(), 1) 1449 1450 community := response.Communities()[0] 1451 1452 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 1453 1454 s.Require().NoError(s.bob.SaveChat(chat)) 1455 1456 message := buildTestMessage(*chat) 1457 message.CommunityID = community.IDString() 1458 1459 // We send a community link to alice 1460 response, err = s.bob.SendChatMessage(ctx, message) 1461 s.Require().NoError(err) 1462 s.Require().NotNil(response) 1463 1464 // Retrieve community link & community 1465 err = tt.RetryWithBackOff(func() error { 1466 response, err = s.alice.RetrieveAll() 1467 if err != nil { 1468 return err 1469 } 1470 if len(response.Communities()) == 0 { 1471 return errors.New("message not received") 1472 } 1473 return nil 1474 }) 1475 1476 s.Require().NoError(err) 1477 1478 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 1479 // We try to join the org 1480 response, err = s.alice.RequestToJoinCommunity(request) 1481 s.Require().NoError(err) 1482 s.Require().NotNil(response) 1483 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1484 1485 requestToJoin1 := response.RequestsToJoinCommunity()[0] 1486 s.Require().NotNil(requestToJoin1) 1487 s.Require().Equal(community.ID(), requestToJoin1.CommunityID) 1488 s.Require().True(requestToJoin1.Our) 1489 s.Require().NotEmpty(requestToJoin1.ID) 1490 s.Require().NotEmpty(requestToJoin1.Clock) 1491 s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1492 s.Require().Equal(requestToJoin1.CustomizationColor, s.alice.account.GetCustomizationColor()) 1493 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State) 1494 1495 // Make sure clock is not empty 1496 s.Require().NotEmpty(requestToJoin1.Clock) 1497 1498 s.Require().Len(response.Communities(), 1) 1499 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock) 1500 1501 // pull all communities to make sure we set RequestedToJoinAt 1502 1503 allCommunities, err := s.alice.Communities() 1504 s.Require().NoError(err) 1505 s.Require().Len(allCommunities, 1) 1506 s.Require().Equal(allCommunities[0].ID(), community.ID()) 1507 s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock) 1508 1509 // pull to make sure it has been saved 1510 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 1511 s.Require().NoError(err) 1512 s.Require().Len(requestsToJoin, 1) 1513 1514 // Make sure the requests are fetched also by community 1515 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 1516 s.Require().NoError(err) 1517 s.Require().Len(requestsToJoin, 1) 1518 1519 // Retrieve request to join 1520 err = tt.RetryWithBackOff(func() error { 1521 response, err = s.bob.RetrieveAll() 1522 if err != nil { 1523 return err 1524 } 1525 if len(response.RequestsToJoinCommunity()) == 0 { 1526 return errors.New("request to join community not received") 1527 } 1528 if len(response.ActivityCenterNotifications()) == 0 { 1529 return errors.New("request to join community notification not added in activity center") 1530 } 1531 return nil 1532 }) 1533 s.Require().NoError(err) 1534 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1535 1536 requestToJoin2 := response.RequestsToJoinCommunity()[0] 1537 1538 s.Require().NotNil(requestToJoin2) 1539 s.Require().Equal(community.ID(), requestToJoin2.CommunityID) 1540 s.Require().False(requestToJoin2.Our) 1541 s.Require().NotEmpty(requestToJoin2.ID) 1542 s.Require().NotEmpty(requestToJoin2.Clock) 1543 s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1544 s.Require().Equal(requestToJoin2.CustomizationColor, s.alice.account.GetCustomizationColor()) 1545 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State) 1546 1547 s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID) 1548 1549 // Cancel request to join community 1550 requestsToJoin, err = s.alice.MyPendingRequestsToJoin() 1551 s.Require().NoError(err) 1552 s.Require().Len(requestsToJoin, 1) 1553 1554 requestToJoin := requestsToJoin[0] 1555 1556 requestToCancel := &requests.CancelRequestToJoinCommunity{ID: requestToJoin.ID} 1557 response, err = s.alice.CancelRequestToJoinCommunity(ctx, requestToCancel) 1558 s.Require().NoError(err) 1559 s.Require().NotNil(response) 1560 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1561 s.Require().Equal(communities.RequestToJoinStateCanceled, response.RequestsToJoinCommunity()[0].State) 1562 1563 // pull to make sure it has been saved 1564 cancelRequestsToJoin, err := s.alice.MyCanceledRequestsToJoin() 1565 s.Require().NoError(err) 1566 s.Require().Len(cancelRequestsToJoin, 1) 1567 s.Require().Equal(cancelRequestsToJoin[0].State, communities.RequestToJoinStateCanceled) 1568 1569 // Retrieve cancel request to join 1570 err = tt.RetryWithBackOff(func() error { 1571 response, err = s.bob.RetrieveAll() 1572 if err != nil { 1573 return err 1574 } 1575 if len(response.RequestsToJoinCommunity()) == 0 { 1576 return errors.New("request to join community not received") 1577 } 1578 return nil 1579 }) 1580 s.Require().NoError(err) 1581 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1582 1583 s.Require().NoError(err) 1584 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1585 1586 // Retrieve activity center notifications for admin to make sure the request notification is deleted 1587 notifications, err := s.bob.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 1588 Cursor: "", 1589 Limit: 10, 1590 ActivityTypes: []ActivityCenterType{}, 1591 ReadType: ActivityCenterQueryParamsReadUnread, 1592 }) 1593 1594 s.Require().NoError(err) 1595 s.Require().Len(notifications.Notifications, 0) 1596 1597 cancelRequestToJoin2 := response.RequestsToJoinCommunity()[0] 1598 1599 s.Require().NotNil(cancelRequestToJoin2) 1600 s.Require().Equal(community.ID(), cancelRequestToJoin2.CommunityID) 1601 s.Require().False(cancelRequestToJoin2.Our) 1602 s.Require().NotEmpty(cancelRequestToJoin2.ID) 1603 s.Require().NotEmpty(cancelRequestToJoin2.Clock) 1604 s.Require().Equal(cancelRequestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1605 s.Require().Equal(cancelRequestToJoin2.CustomizationColor, s.alice.account.GetCustomizationColor()) 1606 s.Require().Equal(communities.RequestToJoinStateCanceled, cancelRequestToJoin2.State) 1607 } 1608 1609 func (s *MessengerCommunitiesSuite) TestRequestAccessAgain() { 1610 description := &requests.CreateCommunity{ 1611 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 1612 Name: "status", 1613 Color: "#ffffff", 1614 Description: "status community description", 1615 } 1616 1617 // Create an community chat 1618 response, err := s.bob.CreateCommunity(description, true) 1619 s.Require().NoError(err) 1620 s.Require().NotNil(response) 1621 s.Require().Len(response.Communities(), 1) 1622 1623 community := response.Communities()[0] 1624 1625 s.advertiseCommunityTo(community, s.bob, s.alice) 1626 1627 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 1628 // We try to join the org 1629 response, err = s.alice.RequestToJoinCommunity(request) 1630 s.Require().NoError(err) 1631 s.Require().NotNil(response) 1632 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1633 1634 s.Require().Len(response.ActivityCenterNotifications(), 1) 1635 1636 notification := response.ActivityCenterNotifications()[0] 1637 s.Require().NotNil(notification) 1638 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 1639 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1640 s.Require().Equal(notification.Read, true) 1641 s.Require().Equal(notification.Accepted, false) 1642 s.Require().Equal(notification.Dismissed, false) 1643 1644 requestToJoin1 := response.RequestsToJoinCommunity()[0] 1645 s.Require().NotNil(requestToJoin1) 1646 s.Require().Equal(community.ID(), requestToJoin1.CommunityID) 1647 s.Require().True(requestToJoin1.Our) 1648 s.Require().NotEmpty(requestToJoin1.ID) 1649 s.Require().NotEmpty(requestToJoin1.Clock) 1650 s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1651 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State) 1652 1653 // Make sure clock is not empty 1654 s.Require().NotEmpty(requestToJoin1.Clock) 1655 1656 s.Require().Len(response.Communities(), 1) 1657 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock) 1658 1659 // pull all communities to make sure we set RequestedToJoinAt 1660 1661 allCommunities, err := s.alice.Communities() 1662 s.Require().NoError(err) 1663 s.Require().Len(allCommunities, 1) 1664 s.Require().Equal(allCommunities[0].ID(), community.ID()) 1665 s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock) 1666 1667 // pull to make sure it has been saved 1668 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 1669 s.Require().NoError(err) 1670 s.Require().Len(requestsToJoin, 1) 1671 1672 // Make sure the requests are fetched also by community 1673 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 1674 s.Require().NoError(err) 1675 s.Require().Len(requestsToJoin, 1) 1676 1677 // Retrieve request to join 1678 err = tt.RetryWithBackOff(func() error { 1679 response, err = s.bob.RetrieveAll() 1680 if err != nil { 1681 return err 1682 } 1683 if len(response.RequestsToJoinCommunity()) == 0 { 1684 return errors.New("request to join community not received") 1685 } 1686 return nil 1687 }) 1688 s.Require().NoError(err) 1689 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1690 1691 requestToJoin2 := response.RequestsToJoinCommunity()[0] 1692 1693 s.Require().NotNil(requestToJoin2) 1694 s.Require().Equal(community.ID(), requestToJoin2.CommunityID) 1695 s.Require().False(requestToJoin2.Our) 1696 s.Require().NotEmpty(requestToJoin2.ID) 1697 s.Require().NotEmpty(requestToJoin2.Clock) 1698 s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1699 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State) 1700 1701 s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID) 1702 1703 // Check that a notification is been added to messenger 1704 1705 notifications := response.Notifications() 1706 s.Require().Len(notifications, 1) 1707 s.Require().NotEqual(notifications[0].ID.Hex(), "0x0000000000000000000000000000000000000000000000000000000000000000") 1708 1709 // Accept request 1710 1711 acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin1.ID} 1712 1713 response, err = s.bob.AcceptRequestToJoinCommunity(acceptRequestToJoin) 1714 s.Require().NoError(err) 1715 s.Require().NotNil(response) 1716 1717 s.Require().Len(response.ActivityCenterNotifications(), 1) 1718 1719 notification = response.ActivityCenterNotifications()[0] 1720 s.Require().NotNil(notification) 1721 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 1722 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted) 1723 s.Require().Equal(notification.Read, true) 1724 s.Require().Equal(notification.Accepted, true) 1725 s.Require().Equal(notification.Dismissed, false) 1726 1727 s.Require().Len(response.Communities(), 1) 1728 1729 updatedCommunity := response.Communities()[0] 1730 1731 s.Require().NotNil(updatedCommunity) 1732 s.Require().True(updatedCommunity.HasMember(&s.alice.identity.PublicKey)) 1733 1734 // Pull message and make sure org is received 1735 err = tt.RetryWithBackOff(func() error { 1736 response, err = s.alice.RetrieveAll() 1737 if err != nil { 1738 return err 1739 } 1740 if len(response.Communities()) == 0 { 1741 return errors.New("community not received") 1742 } 1743 return nil 1744 }) 1745 1746 s.Require().NoError(err) 1747 s.Require().NotNil(response) 1748 1749 s.Require().Len(response.Communities(), 1) 1750 1751 aliceCommunity := response.Communities()[0] 1752 1753 s.Require().Equal(community.ID(), aliceCommunity.ID()) 1754 s.Require().True(aliceCommunity.HasMember(&s.alice.identity.PublicKey)) 1755 1756 // Community should be joined at this point 1757 s.Require().True(aliceCommunity.Joined()) 1758 1759 // Make sure the requests are not pending on either sides 1760 requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID()) 1761 s.Require().NoError(err) 1762 s.Require().Len(requestsToJoin, 0) 1763 1764 requestsToJoin, err = s.alice.MyPendingRequestsToJoin() 1765 s.Require().NoError(err) 1766 s.Require().Len(requestsToJoin, 0) 1767 1768 // We request again 1769 request2 := s.createRequestToJoinCommunity(community.ID(), s.alice) 1770 // We try to join the org, it should error as we are already a member 1771 response, err = s.alice.RequestToJoinCommunity(request2) 1772 s.Require().Error(err) 1773 1774 // We kick the member 1775 response, err = s.bob.RemoveUserFromCommunity( 1776 community.ID(), 1777 common.PubkeyToHex(&s.alice.identity.PublicKey), 1778 ) 1779 s.Require().NoError(err) 1780 s.Require().NotNil(response) 1781 s.Require().Len(response.Communities(), 1) 1782 1783 community = response.Communities()[0] 1784 s.Require().False(community.HasMember(&s.alice.identity.PublicKey)) 1785 1786 // Alice should then be removed 1787 err = tt.RetryWithBackOff(func() error { 1788 response, err = s.alice.RetrieveAll() 1789 if err != nil { 1790 return err 1791 } 1792 if len(response.Communities()) == 0 { 1793 return errors.New("community not received") 1794 } 1795 if len(response.ActivityCenterNotifications()) == 0 { 1796 return errors.New("activity center notification not received") 1797 } 1798 if response.ActivityCenterState().HasSeen { 1799 return errors.New("activity center seen state is incorrect") 1800 } 1801 return nil 1802 }) 1803 1804 // Check we got AC notification for Alice 1805 aliceNotifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 1806 Cursor: "", 1807 Limit: 10, 1808 ActivityTypes: []ActivityCenterType{ActivityCenterNotificationTypeCommunityKicked}, 1809 ReadType: ActivityCenterQueryParamsReadUnread, 1810 }, 1811 ) 1812 s.Require().NoError(err) 1813 s.Require().Len(aliceNotifications.Notifications, 1) 1814 s.Require().Equal(community.IDString(), aliceNotifications.Notifications[0].CommunityID) 1815 1816 s.Require().NoError(err) 1817 s.Require().NotNil(response) 1818 1819 s.Require().Len(response.Communities(), 1) 1820 1821 aliceCommunity = response.Communities()[0] 1822 1823 s.Require().Equal(community.ID(), aliceCommunity.ID()) 1824 s.Require().False(aliceCommunity.HasMember(&s.alice.identity.PublicKey)) 1825 1826 // Alice can request access again 1827 request3 := s.createRequestToJoinCommunity(community.ID(), s.alice) 1828 response, err = s.alice.RequestToJoinCommunity(request3) 1829 s.Require().NoError(err) 1830 s.Require().NotNil(response) 1831 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1832 1833 requestToJoin3 := response.RequestsToJoinCommunity()[0] 1834 s.Require().NotNil(requestToJoin3) 1835 s.Require().Equal(community.ID(), requestToJoin3.CommunityID) 1836 s.Require().True(requestToJoin3.Our) 1837 s.Require().NotEmpty(requestToJoin3.ID) 1838 s.Require().NotEmpty(requestToJoin3.Clock) 1839 s.Require().Equal(requestToJoin3.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1840 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin3.State) 1841 1842 s.Require().Len(response.Communities(), 1) 1843 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin3.Clock) 1844 1845 s.Require().Len(response.ActivityCenterNotifications(), 1) 1846 1847 notification = response.ActivityCenterNotifications()[0] 1848 s.Require().NotNil(notification) 1849 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 1850 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1851 1852 // Retrieve request to join 1853 response, err = WaitOnMessengerResponse(s.bob, 1854 func(r *MessengerResponse) bool { 1855 return len(r.RequestsToJoinCommunity()) == 1 1856 }, 1857 "request to join community was never 1", 1858 ) 1859 s.Require().NoError(err) 1860 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1861 1862 requestToJoin4 := response.RequestsToJoinCommunity()[0] 1863 1864 s.Require().NotNil(requestToJoin4) 1865 s.Require().Equal(community.ID(), requestToJoin4.CommunityID) 1866 s.Require().False(requestToJoin4.Our) 1867 s.Require().NotEmpty(requestToJoin4.ID) 1868 s.Require().NotEmpty(requestToJoin4.Clock) 1869 s.Require().Equal(requestToJoin4.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1870 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin4.State) 1871 1872 s.Require().Equal(requestToJoin3.ID, requestToJoin4.ID) 1873 } 1874 1875 func (s *MessengerCommunitiesSuite) TestDeclineAccess() { 1876 ctx := context.Background() 1877 1878 description := &requests.CreateCommunity{ 1879 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 1880 Name: "status", 1881 Color: "#ffffff", 1882 Description: "status community description", 1883 } 1884 1885 // Create an community chat 1886 response, err := s.bob.CreateCommunity(description, true) 1887 s.Require().NoError(err) 1888 s.Require().NotNil(response) 1889 s.Require().Len(response.Communities(), 1) 1890 1891 community := response.Communities()[0] 1892 1893 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 1894 1895 s.Require().NoError(s.bob.SaveChat(chat)) 1896 1897 message := buildTestMessage(*chat) 1898 message.CommunityID = community.IDString() 1899 1900 // We send a community link to alice 1901 response, err = s.bob.SendChatMessage(ctx, message) 1902 s.Require().NoError(err) 1903 s.Require().NotNil(response) 1904 1905 // Retrieve community link & community 1906 err = tt.RetryWithBackOff(func() error { 1907 response, err = s.alice.RetrieveAll() 1908 if err != nil { 1909 return err 1910 } 1911 if len(response.Communities()) == 0 { 1912 return errors.New("message not received") 1913 } 1914 return nil 1915 }) 1916 1917 s.Require().NoError(err) 1918 1919 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 1920 // We try to join the org 1921 response, err = s.alice.RequestToJoinCommunity(request) 1922 s.Require().NoError(err) 1923 s.Require().NotNil(response) 1924 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1925 1926 requestToJoin1 := response.RequestsToJoinCommunity()[0] 1927 s.Require().NotNil(requestToJoin1) 1928 s.Require().Equal(community.ID(), requestToJoin1.CommunityID) 1929 s.Require().True(requestToJoin1.Our) 1930 s.Require().NotEmpty(requestToJoin1.ID) 1931 s.Require().NotEmpty(requestToJoin1.Clock) 1932 s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1933 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State) 1934 1935 s.Require().Len(response.ActivityCenterNotifications(), 1) 1936 1937 notification := response.ActivityCenterNotifications()[0] 1938 s.Require().NotNil(notification) 1939 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 1940 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 1941 s.Require().Equal(notification.Read, true) 1942 s.Require().Equal(notification.Dismissed, false) 1943 s.Require().Equal(notification.Accepted, false) 1944 1945 // Make sure clock is not empty 1946 s.Require().NotEmpty(requestToJoin1.Clock) 1947 1948 s.Require().Len(response.Communities(), 1) 1949 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock) 1950 1951 // pull all communities to make sure we set RequestedToJoinAt 1952 1953 allCommunities, err := s.alice.Communities() 1954 s.Require().NoError(err) 1955 s.Require().Len(allCommunities, 1) 1956 s.Require().Equal(allCommunities[0].ID(), community.ID()) 1957 s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock) 1958 1959 // Retrieve request to join 1960 err = tt.RetryWithBackOff(func() error { 1961 response, err = s.bob.RetrieveAll() 1962 if err != nil { 1963 return err 1964 } 1965 if len(response.RequestsToJoinCommunity()) == 0 { 1966 return errors.New("request to join community not received") 1967 } 1968 return nil 1969 }) 1970 s.Require().NoError(err) 1971 s.Require().Len(response.RequestsToJoinCommunity(), 1) 1972 1973 // Check if admin sees requests correctly 1974 requestsToJoin, err := s.bob.PendingRequestsToJoinForCommunity(community.ID()) 1975 s.Require().NoError(err) 1976 s.Require().Len(requestsToJoin, 1) 1977 1978 requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID()) 1979 s.Require().NoError(err) 1980 s.Require().Len(requestsToJoin, 0) 1981 1982 requestToJoin2 := response.RequestsToJoinCommunity()[0] 1983 1984 s.Require().NotNil(requestToJoin2) 1985 s.Require().Equal(community.ID(), requestToJoin2.CommunityID) 1986 s.Require().False(requestToJoin2.Our) 1987 s.Require().NotEmpty(requestToJoin2.ID) 1988 s.Require().NotEmpty(requestToJoin2.Clock) 1989 s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 1990 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State) 1991 1992 s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID) 1993 1994 // Decline request 1995 declinedRequestToJoin := &requests.DeclineRequestToJoinCommunity{ID: requestToJoin1.ID} 1996 response, err = s.bob.DeclineRequestToJoinCommunity(declinedRequestToJoin) 1997 s.Require().NoError(err) 1998 s.Require().NotNil(response) 1999 2000 s.Require().Len(response.ActivityCenterNotifications(), 1) 2001 2002 notification = response.ActivityCenterNotifications()[0] 2003 s.Require().NotNil(notification) 2004 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 2005 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusDeclined) 2006 s.Require().Equal(notification.Read, true) 2007 s.Require().Equal(notification.Accepted, false) 2008 s.Require().Equal(notification.Dismissed, true) 2009 2010 // Check if admin sees requests correctly 2011 requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID()) 2012 s.Require().NoError(err) 2013 s.Require().Len(requestsToJoin, 0) 2014 2015 requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID()) 2016 s.Require().NoError(err) 2017 s.Require().Len(requestsToJoin, 1) 2018 2019 // Accept declined request 2020 acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: requestToJoin1.ID} 2021 response, err = s.bob.AcceptRequestToJoinCommunity(acceptRequestToJoin) 2022 s.Require().NoError(err) 2023 s.Require().NotNil(response) 2024 2025 s.Require().Len(response.Communities(), 1) 2026 2027 updatedCommunity := response.Communities()[0] 2028 2029 s.Require().NotNil(updatedCommunity) 2030 s.Require().True(updatedCommunity.HasMember(&s.alice.identity.PublicKey)) 2031 2032 s.Require().Len(response.ActivityCenterNotifications(), 1) 2033 2034 notification = response.ActivityCenterNotifications()[0] 2035 s.Require().NotNil(notification) 2036 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityMembershipRequest) 2037 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusAccepted) 2038 2039 // Pull message and make sure org is received 2040 err = tt.RetryWithBackOff(func() error { 2041 response, err = s.alice.RetrieveAll() 2042 if err != nil { 2043 return err 2044 } 2045 if len(response.Communities()) == 0 { 2046 return errors.New("community not received") 2047 } 2048 return nil 2049 }) 2050 2051 s.Require().NoError(err) 2052 s.Require().NotNil(response) 2053 2054 s.Require().Len(response.Communities(), 1) 2055 2056 aliceCommunity := response.Communities()[0] 2057 2058 s.Require().Equal(community.ID(), aliceCommunity.ID()) 2059 s.Require().True(aliceCommunity.HasMember(&s.alice.identity.PublicKey)) 2060 2061 // Community should be joined at this point 2062 s.Require().True(aliceCommunity.Joined()) 2063 2064 // Make sure the requests are not pending on either sides 2065 requestsToJoin, err = s.bob.PendingRequestsToJoinForCommunity(community.ID()) 2066 s.Require().NoError(err) 2067 s.Require().Len(requestsToJoin, 0) 2068 2069 requestsToJoin, err = s.bob.DeclinedRequestsToJoinForCommunity(community.ID()) 2070 s.Require().NoError(err) 2071 s.Require().Len(requestsToJoin, 0) 2072 2073 requestsToJoin, err = s.alice.MyPendingRequestsToJoin() 2074 s.Require().NoError(err) 2075 s.Require().Len(requestsToJoin, 0) 2076 } 2077 2078 func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() { 2079 community, _ := s.createCommunity() 2080 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice) 2081 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.bob) 2082 2083 s.joinCommunity(community, s.owner, s.alice) 2084 s.joinCommunity(community, s.owner, s.bob) 2085 2086 joinedCommunities, err := s.owner.communitiesManager.Joined() 2087 s.Require().NoError(err) 2088 s.Require().Equal(3, joinedCommunities[0].MembersCount()) 2089 2090 response, err := s.alice.LeaveCommunity(community.ID()) 2091 s.Require().NoError(err) 2092 s.Require().NotNil(response) 2093 s.Require().Len(response.Communities(), 1) 2094 s.Require().False(response.Communities()[0].Joined()) 2095 2096 // admin should receive alice's request to leave 2097 // and then update and advertise community members list accordingly 2098 2099 verifyCommunityMembers := func(user *Messenger) error { 2100 response, err := user.RetrieveAll() 2101 if err != nil { 2102 return err 2103 } 2104 2105 if len(response.Communities()) == 0 { 2106 return errors.New("no communities in response") 2107 } 2108 2109 var communityMembersError error = nil 2110 2111 if response.Communities()[0].MembersCount() != 2 { 2112 communityMembersError = fmt.Errorf("invalid number of members: %d", response.Communities()[0].MembersCount()) 2113 } else if !response.Communities()[0].HasMember(&s.owner.identity.PublicKey) { 2114 communityMembersError = errors.New("admin removed from community") 2115 } else if !response.Communities()[0].HasMember(&s.bob.identity.PublicKey) { 2116 communityMembersError = errors.New("bob removed from community") 2117 } else if response.Communities()[0].HasMember(&s.alice.identity.PublicKey) { 2118 communityMembersError = errors.New("alice not removed from community") 2119 } 2120 2121 return communityMembersError 2122 } 2123 err = tt.RetryWithBackOff(func() error { 2124 return verifyCommunityMembers(s.owner) 2125 }) 2126 s.Require().NoError(err) 2127 err = tt.RetryWithBackOff(func() error { 2128 return verifyCommunityMembers(s.bob) 2129 }) 2130 s.Require().NoError(err) 2131 2132 joinedCommunities, err = s.owner.communitiesManager.Joined() 2133 s.Require().NoError(err) 2134 s.Require().Equal(2, joinedCommunities[0].MembersCount()) 2135 2136 chats, err := s.alice.persistence.Chats() 2137 s.Require().NoError(err) 2138 var numberInactiveChats = 0 2139 for i := 0; i < len(chats); i++ { 2140 if !chats[i].Active { 2141 numberInactiveChats++ 2142 } 2143 } 2144 s.Require().Equal(2, numberInactiveChats) 2145 2146 // alice can rejoin 2147 s.joinCommunity(community, s.owner, s.alice) 2148 2149 joinedCommunities, err = s.owner.communitiesManager.Joined() 2150 s.Require().NoError(err) 2151 s.Require().Equal(3, joinedCommunities[0].MembersCount()) 2152 2153 chats, err = s.alice.persistence.Chats() 2154 s.Require().NoError(err) 2155 numberInactiveChats = 0 2156 for i := 0; i < len(chats); i++ { 2157 if !chats[i].Active { 2158 numberInactiveChats++ 2159 } 2160 } 2161 s.Require().Equal(1, numberInactiveChats) 2162 } 2163 2164 func (s *MessengerCommunitiesSuite) TestShareCommunity() { 2165 description := &requests.CreateCommunity{ 2166 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 2167 Name: "status", 2168 Description: "status community description", 2169 Color: "#FFFFFF", 2170 Image: "../_assets/tests/status.png", 2171 ImageAx: 0, 2172 ImageAy: 0, 2173 ImageBx: 256, 2174 ImageBy: 256, 2175 Banner: images.CroppedImage{ 2176 ImagePath: "../_assets/tests/IMG_1205.HEIC.jpg", 2177 X: 0, 2178 Y: 0, 2179 Width: 160, 2180 Height: 90, 2181 }, 2182 } 2183 2184 response, err := s.owner.CreateCommunity(description, true) 2185 2186 s.Require().NoError(err) 2187 s.Require().NotNil(response) 2188 s.Require().Len(response.Communities(), 1) 2189 s.Require().Len(response.Chats(), 1) 2190 community := response.Communities()[0] 2191 2192 inputMessageText := "Come on alice, You'll like it here!" 2193 // Alice shares community with Bob 2194 response, err = s.owner.ShareCommunity(&requests.ShareCommunity{ 2195 CommunityID: community.ID(), 2196 Users: []types.HexBytes{common.PubkeyToHexBytes(&s.alice.identity.PublicKey)}, 2197 InviteMessage: inputMessageText, 2198 }) 2199 2200 s.Require().NoError(err) 2201 s.Require().NotNil(response) 2202 s.Require().Len(response.Messages(), 1) 2203 sentMessageText := response.Messages()[0].Text 2204 2205 _, err = WaitOnMessengerResponse(s.alice, func(r *MessengerResponse) bool { 2206 return len(r.Messages()) > 0 2207 }, "Messages not received") 2208 2209 communityURL := response.Messages()[0].UnfurledStatusLinks.GetUnfurledStatusLinks()[0].Url 2210 s.Require().NoError(err) 2211 s.Require().Len(response.Messages(), 1) 2212 s.Require().Equal(fmt.Sprintf("%s\n%s", inputMessageText, communityURL), sentMessageText) 2213 s.Require().NotNil(response.Messages()[0].UnfurledStatusLinks.GetUnfurledStatusLinks()[0].GetCommunity().CommunityId) 2214 } 2215 2216 func (s *MessengerCommunitiesSuite) TestShareCommunityWithPreviousMember() { 2217 description := &requests.CreateCommunity{ 2218 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 2219 Name: "status", 2220 Color: "#ffffff", 2221 Description: "status community description", 2222 } 2223 2224 // Create an community chat 2225 response, err := s.bob.CreateCommunity(description, true) 2226 s.Require().NoError(err) 2227 s.Require().NotNil(response) 2228 s.Require().Len(response.Communities(), 1) 2229 2230 community := response.Communities()[0] 2231 2232 orgChat := &protobuf.CommunityChat{ 2233 Permissions: &protobuf.CommunityPermissions{ 2234 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 2235 }, 2236 Identity: &protobuf.ChatIdentity{ 2237 DisplayName: "status-core", 2238 Emoji: "😎", 2239 Description: "status-core community chat", 2240 }, 2241 } 2242 response, err = s.bob.CreateCommunityChat(community.ID(), orgChat) 2243 s.Require().NoError(err) 2244 s.Require().NotNil(response) 2245 s.Require().Len(response.Communities(), 1) 2246 s.Require().Len(response.Chats(), 1) 2247 2248 community = response.Communities()[0] 2249 communityChat := response.Chats()[0] 2250 2251 // Add Alice to the community before sharing it 2252 _, err = community.AddMember(&s.alice.identity.PublicKey, []protobuf.CommunityMember_Roles{}, community.Clock()) 2253 s.Require().NoError(err) 2254 2255 err = s.bob.communitiesManager.SaveCommunity(community) 2256 s.Require().NoError(err) 2257 2258 advertiseCommunityToUserOldWay(&s.Suite, community, s.bob, s.alice) 2259 2260 // Add bob to contacts so it does not go on activity center 2261 bobPk := common.PubkeyToHex(&s.bob.identity.PublicKey) 2262 request := &requests.AddContact{ID: bobPk} 2263 _, err = s.alice.AddContact(context.Background(), request) 2264 s.Require().NoError(err) 2265 2266 // Alice should have the Joined status for the community 2267 communityInResponse := response.Communities()[0] 2268 s.Require().Equal(community.ID(), communityInResponse.ID()) 2269 s.Require().True(communityInResponse.Joined()) 2270 2271 // Alice is able to receive messages in the community 2272 inputMessage := buildTestMessage(*communityChat) 2273 sendResponse, err := s.bob.SendChatMessage(context.Background(), inputMessage) 2274 messageID := sendResponse.Messages()[0].ID 2275 s.NoError(err) 2276 s.Require().Len(sendResponse.Messages(), 1) 2277 2278 response, err = WaitOnMessengerResponse( 2279 s.alice, 2280 func(r *MessengerResponse) bool { return len(r.messages) > 0 }, 2281 "no messages", 2282 ) 2283 s.Require().NoError(err) 2284 s.Require().Len(response.Chats(), 1) 2285 s.Require().Len(response.Messages(), 1) 2286 s.Require().Equal(messageID, response.Messages()[0].ID) 2287 } 2288 2289 func (s *MessengerCommunitiesSuite) TestBanUser() { 2290 community, _ := s.createCommunity() 2291 2292 s.advertiseCommunityTo(community, s.owner, s.alice) 2293 s.joinCommunity(community, s.owner, s.alice) 2294 2295 response, err := s.owner.BanUserFromCommunity( 2296 context.Background(), 2297 &requests.BanUserFromCommunity{ 2298 CommunityID: community.ID(), 2299 User: common.PubkeyToHexBytes(&s.alice.identity.PublicKey), 2300 }, 2301 ) 2302 s.Require().NoError(err) 2303 s.Require().NotNil(response) 2304 s.Require().Len(response.Communities(), 1) 2305 2306 community = response.Communities()[0] 2307 s.Require().False(community.HasMember(&s.alice.identity.PublicKey)) 2308 s.Require().True(community.IsBanned(&s.alice.identity.PublicKey)) 2309 s.Require().Len(community.PendingAndBannedMembers(), 1) 2310 s.Require().Equal(community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()], communities.CommunityMemberBanned) 2311 2312 response, err = WaitOnMessengerResponse( 2313 s.alice, 2314 func(r *MessengerResponse) bool { 2315 return len(r.Communities()) == 1 && 2316 len(r.Communities()[0].PendingAndBannedMembers()) == 1 && 2317 community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()] == communities.CommunityMemberBanned && 2318 r.Communities()[0].IsBanned(&s.alice.identity.PublicKey) && 2319 len(r.ActivityCenterNotifications()) == 1 && 2320 !r.ActivityCenterState().HasSeen && 2321 !r.Communities()[0].Spectated() && 2322 !r.Communities()[0].Joined() 2323 2324 }, 2325 "no message about alice ban", 2326 ) 2327 2328 s.Require().NoError(err) 2329 s.Require().NotNil(response) 2330 2331 // Check we got ban AC notification for Alice 2332 aliceNotifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 2333 Cursor: "", 2334 Limit: 10, 2335 ActivityTypes: []ActivityCenterType{ActivityCenterNotificationTypeCommunityBanned}, 2336 ReadType: ActivityCenterQueryParamsReadUnread, 2337 }, 2338 ) 2339 s.Require().NoError(err) 2340 s.Require().Len(aliceNotifications.Notifications, 1) 2341 s.Require().Equal(community.IDString(), aliceNotifications.Notifications[0].CommunityID) 2342 2343 response, err = s.owner.UnbanUserFromCommunity( 2344 &requests.UnbanUserFromCommunity{ 2345 CommunityID: community.ID(), 2346 User: common.PubkeyToHexBytes(&s.alice.identity.PublicKey), 2347 }, 2348 ) 2349 s.Require().NoError(err) 2350 s.Require().NotNil(response) 2351 s.Require().Len(response.Communities(), 1) 2352 2353 community = response.Communities()[0] 2354 s.Require().False(community.HasMember(&s.alice.identity.PublicKey)) 2355 s.Require().False(community.IsBanned(&s.alice.identity.PublicKey)) 2356 s.Require().Len(community.PendingAndBannedMembers(), 0) 2357 2358 response, err = WaitOnMessengerResponse( 2359 s.alice, 2360 func(r *MessengerResponse) bool { 2361 return len(r.Communities()) == 1 && 2362 len(r.Communities()[0].PendingAndBannedMembers()) == 0 && 2363 !r.Communities()[0].IsBanned(&s.alice.identity.PublicKey) && 2364 len(r.ActivityCenterNotifications()) == 1 && !r.ActivityCenterState().HasSeen 2365 }, 2366 "no message about alice unban", 2367 ) 2368 2369 s.Require().NoError(err) 2370 s.Require().NotNil(response) 2371 s.Require().Len(response.Communities(), 1) 2372 s.Require().False(response.Communities()[0].Joined()) 2373 2374 // Check we got unban AC notification for Alice 2375 aliceNotifications, err = s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 2376 Cursor: "", 2377 Limit: 10, 2378 ActivityTypes: []ActivityCenterType{ActivityCenterNotificationTypeCommunityUnbanned}, 2379 ReadType: ActivityCenterQueryParamsReadUnread, 2380 }, 2381 ) 2382 s.Require().NoError(err) 2383 s.Require().Len(aliceNotifications.Notifications, 1) 2384 s.Require().Equal(community.IDString(), aliceNotifications.Notifications[0].CommunityID) 2385 } 2386 2387 func (s *MessengerCommunitiesSuite) createOtherDevice(m1 *Messenger) *Messenger { 2388 userPk := m1.IdentityPublicKeyString() 2389 addresses, exists := s.accountsTestData[userPk] 2390 s.Require().True(exists) 2391 password, exists := s.accountsPasswords[userPk] 2392 s.Require().True(exists) 2393 m2 := s.newMessengerWithKey(m1.identity, password, addresses) 2394 2395 tcs, err := m2.communitiesManager.All() 2396 s.Require().NoError(err, "m2.communitiesManager.All") 2397 s.Len(tcs, 0, "Must have 0 communities") 2398 2399 // Pair devices 2400 metadata := &multidevice.InstallationMetadata{ 2401 Name: "other-device", 2402 DeviceType: "other-device-type", 2403 } 2404 err = m2.SetInstallationMetadata(m2.installationID, metadata) 2405 s.Require().NoError(err) 2406 2407 _, err = m2.Start() 2408 s.Require().NoError(err) 2409 2410 return m2 2411 } 2412 2413 func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings() { 2414 // Create new device 2415 alicesOtherDevice := s.createOtherDevice(s.alice) 2416 PairDevices(&s.Suite, alicesOtherDevice, s.alice) 2417 2418 // Create a community 2419 createCommunityReq := &requests.CreateCommunity{ 2420 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 2421 Name: "new community", 2422 Color: "#000000", 2423 Description: "new community description", 2424 } 2425 2426 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 2427 s.Require().NoError(err, "s.alice.CreateCommunity") 2428 var newCommunity *communities.Community 2429 for _, com := range mr.Communities() { 2430 if com.Name() == createCommunityReq.Name { 2431 newCommunity = com 2432 } 2433 } 2434 s.Require().NotNil(newCommunity) 2435 2436 // Check that Alice has community settings 2437 cs, err := s.alice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2438 s.Require().NoError(err, "communitiesManager.GetCommunitySettingsByID") 2439 s.NotNil(cs, "Must have community settings") 2440 2441 // Wait for the message to reach its destination 2442 err = tt.RetryWithBackOff(func() error { 2443 _, err = alicesOtherDevice.RetrieveAll() 2444 if err != nil { 2445 return err 2446 } 2447 2448 // Do we have new synced community settings? 2449 syncedSettings, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2450 if err != nil || syncedSettings == nil { 2451 return fmt.Errorf("community with sync not received %w", err) 2452 } 2453 return nil 2454 }) 2455 s.Require().NoError(err) 2456 2457 tcs, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2458 s.Require().NoError(err) 2459 2460 // Check the community settings on their device matched the community settings on Alice's device 2461 s.Equal(cs.CommunityID, tcs.CommunityID) 2462 s.Equal(cs.HistoryArchiveSupportEnabled, tcs.HistoryArchiveSupportEnabled) 2463 } 2464 2465 func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings_EditCommunity() { 2466 // Create new device 2467 alicesOtherDevice := s.createOtherDevice(s.alice) 2468 PairDevices(&s.Suite, alicesOtherDevice, s.alice) 2469 2470 // Create a community 2471 createCommunityReq := &requests.CreateCommunity{ 2472 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 2473 Name: "new community", 2474 Color: "#000000", 2475 Description: "new community description", 2476 } 2477 2478 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 2479 s.Require().NoError(err, "s.alice.CreateCommunity") 2480 var newCommunity *communities.Community 2481 for _, com := range mr.Communities() { 2482 if com.Name() == createCommunityReq.Name { 2483 newCommunity = com 2484 } 2485 } 2486 s.Require().NotNil(newCommunity) 2487 2488 // Check that Alice has community settings 2489 cs, err := s.alice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2490 s.Require().NoError(err, "communitiesManager.GetCommunitySettingsByID") 2491 s.NotNil(cs, "Must have community settings") 2492 2493 // Wait for the message to reach its destination 2494 err = tt.RetryWithBackOff(func() error { 2495 _, err = alicesOtherDevice.RetrieveAll() 2496 if err != nil { 2497 return err 2498 } 2499 2500 // Do we have new synced community settings? 2501 syncedSettings, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2502 if err != nil || syncedSettings == nil { 2503 return fmt.Errorf("community settings with sync not received %w", err) 2504 } 2505 return nil 2506 }) 2507 s.Require().NoError(err) 2508 2509 tcs, err := alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2510 s.Require().NoError(err) 2511 2512 // Check the community settings on their device matched the community settings on Alice's device 2513 s.Equal(cs.CommunityID, tcs.CommunityID) 2514 s.Equal(cs.HistoryArchiveSupportEnabled, tcs.HistoryArchiveSupportEnabled) 2515 2516 req := createCommunityReq 2517 req.HistoryArchiveSupportEnabled = true 2518 editCommunityReq := &requests.EditCommunity{ 2519 CommunityID: newCommunity.ID(), 2520 CreateCommunity: *req, 2521 } 2522 2523 mr, err = s.alice.EditCommunity(editCommunityReq) 2524 s.Require().NoError(err, "s.alice.EditCommunity") 2525 var editedCommunity *communities.Community 2526 for _, com := range mr.Communities() { 2527 if com.Name() == createCommunityReq.Name { 2528 editedCommunity = com 2529 } 2530 } 2531 s.Require().NotNil(editedCommunity) 2532 2533 // Wait a bit for sync messages to reach destination 2534 time.Sleep(1 * time.Second) 2535 err = tt.RetryWithBackOff(func() error { 2536 _, err = alicesOtherDevice.RetrieveAll() 2537 if err != nil { 2538 return err 2539 } 2540 return nil 2541 }) 2542 s.Require().NoError(err) 2543 2544 tcs, err = alicesOtherDevice.communitiesManager.GetCommunitySettingsByID(newCommunity.ID()) 2545 s.Require().NoError(err) 2546 2547 // Check the community settings on their device matched the community settings on Alice's device 2548 s.Equal(cs.CommunityID, tcs.CommunityID) 2549 s.Equal(req.HistoryArchiveSupportEnabled, tcs.HistoryArchiveSupportEnabled) 2550 } 2551 2552 // TestSyncCommunity tests basic sync functionality between 2 Messengers 2553 func (s *MessengerCommunitiesSuite) TestSyncCommunity() { 2554 2555 // Create new device 2556 alicesOtherDevice := s.createOtherDevice(s.alice) 2557 PairDevices(&s.Suite, alicesOtherDevice, s.alice) 2558 2559 // Create a community 2560 createCommunityReq := &requests.CreateCommunity{ 2561 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 2562 Name: "new community", 2563 Color: "#000000", 2564 Description: "new community description", 2565 } 2566 2567 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 2568 s.Require().NoError(err, "s.alice.CreateCommunity") 2569 var newCommunity *communities.Community 2570 for _, com := range mr.Communities() { 2571 if com.Name() == createCommunityReq.Name { 2572 newCommunity = com 2573 } 2574 } 2575 s.Require().NotNil(newCommunity) 2576 2577 // Check that Alice has 1 community 2578 cs, err := s.alice.communitiesManager.All() 2579 s.Require().NoError(err, "communitiesManager.All") 2580 s.Len(cs, 1, "Must have 1 community") 2581 2582 // Wait for the message to reach its destination 2583 err = tt.RetryWithBackOff(func() error { 2584 _, err = alicesOtherDevice.RetrieveAll() 2585 if err != nil { 2586 return err 2587 } 2588 2589 // Do we have a new synced community? 2590 _, err = alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(newCommunity.ID()) 2591 if err != nil { 2592 return fmt.Errorf("community with sync not received %w", err) 2593 } 2594 2595 return nil 2596 }) 2597 s.Require().NoError(err) 2598 2599 // Count the number of communities in their device 2600 tcs, err := alicesOtherDevice.communitiesManager.All() 2601 s.Require().NoError(err) 2602 s.Len(tcs, 1, "There must be 1 community") 2603 2604 s.logger.Debug("", zap.Any("tcs", tcs)) 2605 2606 // Get the new community from their db 2607 tnc, err := alicesOtherDevice.communitiesManager.GetByID(newCommunity.ID()) 2608 s.Require().NoError(err) 2609 2610 // Check the community on their device matched the new community on Alice's device 2611 s.Equal(newCommunity.ID(), tnc.ID()) 2612 s.Equal(newCommunity.Name(), tnc.Name()) 2613 s.Equal(newCommunity.DescriptionText(), tnc.DescriptionText()) 2614 s.Equal(newCommunity.IDString(), tnc.IDString()) 2615 2616 // Private Key for synced community should be null 2617 s.Require().NotNil(newCommunity.PrivateKey()) 2618 s.Require().Nil(tnc.PrivateKey()) 2619 2620 s.Equal(newCommunity.PublicKey(), tnc.PublicKey()) 2621 s.Equal(newCommunity.Verified(), tnc.Verified()) 2622 s.Equal(newCommunity.Muted(), tnc.Muted()) 2623 s.Equal(newCommunity.Joined(), tnc.Joined()) 2624 s.Equal(newCommunity.Spectated(), tnc.Spectated()) 2625 2626 s.True(newCommunity.IsControlNode()) 2627 s.True(newCommunity.IsOwner()) 2628 2629 // Even though synced device have the private key, it is not the control node 2630 // There can be only one control node 2631 s.False(tnc.IsControlNode()) 2632 s.True(tnc.IsOwner()) 2633 } 2634 2635 func (s *MessengerCommunitiesSuite) TestSyncCommunity_EncryptionKeys() { 2636 // Create new device 2637 ownersOtherDevice := s.createOtherDevice(s.owner) 2638 defer TearDownMessenger(&s.Suite, ownersOtherDevice) 2639 2640 PairDevices(&s.Suite, ownersOtherDevice, s.owner) 2641 2642 community, chat := s.createCommunity() 2643 s.owner.communitiesManager.RekeyInterval = 1 * time.Hour 2644 2645 { // ensure both community and channel are encrypted 2646 permissionRequest := requests.CreateCommunityTokenPermission{ 2647 CommunityID: community.ID(), 2648 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 2649 TokenCriteria: []*protobuf.TokenCriteria{ 2650 &protobuf.TokenCriteria{ 2651 Type: protobuf.CommunityTokenType_ERC20, 2652 ContractAddresses: map[uint64]string{testChainID1: "0x123"}, 2653 Symbol: "TEST", 2654 AmountInWei: "100000000000000000000", 2655 Decimals: uint64(18), 2656 }, 2657 }, 2658 } 2659 _, err := s.owner.CreateCommunityTokenPermission(&permissionRequest) 2660 s.Require().NoError(err) 2661 2662 channelPermissionRequest := requests.CreateCommunityTokenPermission{ 2663 CommunityID: community.ID(), 2664 Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL, 2665 TokenCriteria: []*protobuf.TokenCriteria{ 2666 &protobuf.TokenCriteria{ 2667 Type: protobuf.CommunityTokenType_ERC20, 2668 ContractAddresses: map[uint64]string{testChainID1: "0x123"}, 2669 Symbol: "TEST", 2670 AmountInWei: "100000000000000000000", 2671 Decimals: uint64(18), 2672 }, 2673 }, 2674 ChatIds: []string{chat.ID}, 2675 } 2676 2677 _, err = s.owner.CreateCommunityTokenPermission(&channelPermissionRequest) 2678 s.Require().NoError(err) 2679 } 2680 2681 getKeysCount := func(m *Messenger) (communityKeysCount int, channelKeysCount int) { 2682 keys, err := m.encryptor.GetAllHRKeys(community.ID()) 2683 s.Require().NoError(err) 2684 if keys != nil { 2685 communityKeysCount = len(keys.Keys) 2686 } 2687 2688 channelKeys, err := m.encryptor.GetAllHRKeys([]byte(community.IDString() + chat.CommunityChatID())) 2689 s.Require().NoError(err) 2690 if channelKeys != nil { 2691 channelKeysCount = len(channelKeys.Keys) 2692 } 2693 return 2694 } 2695 2696 communityKeysCount, channelKeysCount := getKeysCount(s.owner) 2697 s.Require().GreaterOrEqual(communityKeysCount, 1) 2698 s.Require().GreaterOrEqual(channelKeysCount, 1) 2699 2700 // ensure both community and channel keys are synced 2701 _, err := WaitOnMessengerResponse(ownersOtherDevice, func(mr *MessengerResponse) bool { 2702 communityKeysCount, channelKeysCount := getKeysCount(s.owner) 2703 syncedCommunityKeysCount, syncedChannelKeysCount := getKeysCount(ownersOtherDevice) 2704 2705 return communityKeysCount == syncedCommunityKeysCount && channelKeysCount == syncedChannelKeysCount 2706 }, "keys not synced") 2707 s.Require().NoError(err) 2708 } 2709 2710 // TestSyncCommunity_RequestToJoin tests more complex pairing and syncing scenario where one paired device 2711 // makes a request to join a community 2712 func (s *MessengerCommunitiesSuite) TestSyncCommunity_RequestToJoin() { 2713 // Set Alice's installation metadata 2714 aim := &multidevice.InstallationMetadata{ 2715 Name: "alice's-device", 2716 DeviceType: "alice's-device-type", 2717 } 2718 err := s.alice.SetInstallationMetadata(s.alice.installationID, aim) 2719 s.Require().NoError(err) 2720 2721 // Create Alice's other device 2722 alicesOtherDevice := s.createOtherDevice(s.alice) 2723 2724 // Pair alice's two devices 2725 PairDevices(&s.Suite, alicesOtherDevice, s.alice) 2726 PairDevices(&s.Suite, s.alice, alicesOtherDevice) 2727 2728 // Check bob the admin has 0 community 2729 tcs2, err := s.bob.communitiesManager.All() 2730 s.Require().NoError(err, "admin.communitiesManager.All") 2731 s.Len(tcs2, 0, "Must have 0 communities") 2732 2733 // Bob the admin creates a community 2734 createCommunityReq := &requests.CreateCommunity{ 2735 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 2736 Name: "new community", 2737 Color: "#000000", 2738 Description: "new community description", 2739 } 2740 mr, err := s.bob.CreateCommunity(createCommunityReq, true) 2741 s.Require().NoError(err, "CreateCommunity") 2742 s.Require().NotNil(mr) 2743 s.Len(mr.Communities(), 1) 2744 2745 community := mr.Communities()[0] 2746 2747 // Check that admin has 1 community 2748 acs, err := s.bob.communitiesManager.All() 2749 s.Require().NoError(err, "communitiesManager.All") 2750 s.Len(acs, 1, "Must have 1 communities") 2751 2752 // Check that Alice has 0 communities on either device 2753 cs, err := s.alice.communitiesManager.All() 2754 s.Require().NoError(err, "communitiesManager.All") 2755 s.Len(cs, 0, "Must have 0 communities") 2756 2757 tcs1, err := alicesOtherDevice.communitiesManager.All() 2758 s.Require().NoError(err, "alicesOtherDevice.communitiesManager.All") 2759 s.Len(tcs1, 0, "Must have 0 communities") 2760 2761 // Bob the admin opens up a 1-1 chat with alice 2762 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 2763 s.Require().NoError(s.bob.SaveChat(chat)) 2764 2765 // Bob the admin shares with Alice, via public chat, an invite link to the new community 2766 message := buildTestMessage(*chat) 2767 message.CommunityID = community.IDString() 2768 response, err := s.bob.SendChatMessage(context.Background(), message) 2769 s.Require().NoError(err) 2770 s.Require().NotNil(response) 2771 2772 // Retrieve community link & community 2773 err = tt.RetryWithBackOff(func() error { 2774 response, err = s.alice.RetrieveAll() 2775 if err != nil { 2776 return err 2777 } 2778 if len(response.Communities()) == 0 { 2779 return errors.New("no communities received from 1-1") 2780 } 2781 return nil 2782 }) 2783 s.Require().NoError(err) 2784 2785 // Check that alice now has 1 community 2786 cs, err = s.alice.communitiesManager.All() 2787 s.Require().NoError(err, "communitiesManager.All") 2788 s.Len(cs, 1, "Must have 1 community") 2789 for _, c := range cs { 2790 s.False(c.Joined(), "Must not have joined the community") 2791 } 2792 2793 // Alice requests to join the new community 2794 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 2795 response, err = s.alice.RequestToJoinCommunity(request) 2796 s.Require().NoError(err) 2797 s.Require().NotNil(response) 2798 s.Require().Len(response.RequestsToJoinCommunity(), 1) 2799 2800 s.Require().Len(response.ActivityCenterNotifications(), 1) 2801 2802 notification := response.ActivityCenterNotifications()[0] 2803 s.Require().NotNil(notification) 2804 s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest) 2805 s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending) 2806 2807 aRtj := response.RequestsToJoinCommunity()[0] 2808 s.Require().NotNil(aRtj) 2809 s.Equal(community.ID(), aRtj.CommunityID) 2810 s.True(aRtj.Our) 2811 s.Require().NotEmpty(aRtj.ID) 2812 s.Require().NotEmpty(aRtj.Clock) 2813 s.Equal(aRtj.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 2814 s.Equal(aRtj.CustomizationColor, s.alice.account.GetCustomizationColor()) 2815 s.Equal(communities.RequestToJoinStatePending, aRtj.State) 2816 2817 // Make sure clock is not empty 2818 s.Require().NotEmpty(aRtj.Clock) 2819 2820 s.Len(response.Communities(), 1) 2821 s.Equal(response.Communities()[0].RequestedToJoinAt(), aRtj.Clock) 2822 2823 // pull all communities to make sure we set RequestedToJoinAt 2824 allCommunities, err := s.alice.Communities() 2825 s.Require().NoError(err) 2826 s.Len(allCommunities, 1) 2827 s.Require().Equal(allCommunities[0].ID(), community.ID()) 2828 s.Require().Equal(allCommunities[0].RequestedToJoinAt(), aRtj.Clock) 2829 2830 // pull to make sure it has been saved 2831 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 2832 s.Require().NoError(err) 2833 s.Len(requestsToJoin, 1) 2834 2835 // Make sure the requests are fetched also by community 2836 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 2837 s.Require().NoError(err) 2838 s.Len(requestsToJoin, 1) 2839 2840 // Alice's other device retrieves sync message from the join 2841 err = tt.RetryWithBackOff(func() error { 2842 response, err = alicesOtherDevice.RetrieveAll() 2843 if err != nil { 2844 return err 2845 } 2846 2847 // Do we have a new synced community? 2848 _, err = alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(community.ID()) 2849 if err != nil { 2850 return fmt.Errorf("community with sync not received %w", err) 2851 } 2852 2853 // Do we have a new pending request to join for the new community 2854 requestsToJoin, err = alicesOtherDevice.PendingRequestsToJoinForCommunity(community.ID()) 2855 if err != nil { 2856 return err 2857 } 2858 if len(requestsToJoin) == 0 { 2859 return errors.New("no requests to join") 2860 } 2861 2862 return nil 2863 }) 2864 s.Require().NoError(err) 2865 s.Len(response.Communities(), 1) 2866 2867 // Get the pending requests to join for the new community on alicesOtherDevice 2868 requestsToJoin, err = alicesOtherDevice.PendingRequestsToJoinForCommunity(community.ID()) 2869 s.Require().NoError(err) 2870 s.Len(requestsToJoin, 1) 2871 2872 // Check request to join on alicesOtherDevice matches the RTJ on alice 2873 aodRtj := requestsToJoin[0] 2874 s.Equal(aRtj.PublicKey, aodRtj.PublicKey) 2875 s.Equal(aRtj.ID, aodRtj.ID) 2876 s.Equal(aRtj.CommunityID, aodRtj.CommunityID) 2877 s.Equal(aRtj.Clock, aodRtj.Clock) 2878 s.Equal(aRtj.ENSName, aodRtj.ENSName) 2879 s.Equal(aRtj.ChatID, aodRtj.ChatID) 2880 s.Equal(aRtj.State, aodRtj.State) 2881 s.Equal(aRtj.CustomizationColor, aodRtj.CustomizationColor) 2882 2883 // Bob the admin retrieves request to join 2884 err = tt.RetryWithBackOff(func() error { 2885 response, err = s.bob.RetrieveAll() 2886 if err != nil { 2887 return err 2888 } 2889 if len(response.RequestsToJoinCommunity()) == 0 { 2890 return errors.New("request to join community not received") 2891 } 2892 return nil 2893 }) 2894 s.Require().NoError(err) 2895 s.Len(response.RequestsToJoinCommunity(), 1) 2896 2897 // Check that bob the admin's newly received request to join matches what we expect 2898 bobRtj := response.RequestsToJoinCommunity()[0] 2899 s.Require().NotNil(bobRtj) 2900 s.Equal(community.ID(), bobRtj.CommunityID) 2901 s.False(bobRtj.Our) 2902 s.Require().NotEmpty(bobRtj.ID) 2903 s.Require().NotEmpty(bobRtj.Clock) 2904 s.Equal(bobRtj.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 2905 s.Equal(bobRtj.CustomizationColor, s.alice.account.GetCustomizationColor()) 2906 s.Equal(communities.RequestToJoinStatePending, bobRtj.State) 2907 2908 s.Equal(aRtj.PublicKey, bobRtj.PublicKey) 2909 s.Equal(aRtj.ID, bobRtj.ID) 2910 s.Equal(aRtj.CommunityID, bobRtj.CommunityID) 2911 s.Equal(aRtj.Clock, bobRtj.Clock) 2912 s.Equal(aRtj.ENSName, bobRtj.ENSName) 2913 s.Equal(aRtj.ChatID, bobRtj.ChatID) 2914 s.Equal(aRtj.State, bobRtj.State) 2915 s.Equal(aRtj.CustomizationColor, bobRtj.CustomizationColor) 2916 } 2917 2918 func (s *MessengerCommunitiesSuite) TestSyncCommunity_Leave() { 2919 // Set Alice's installation metadata 2920 aim := &multidevice.InstallationMetadata{ 2921 Name: "alice's-device", 2922 DeviceType: "alice's-device-type", 2923 } 2924 err := s.alice.SetInstallationMetadata(s.alice.installationID, aim) 2925 s.Require().NoError(err) 2926 2927 // Create Alice's other device 2928 alicesOtherDevice := s.createOtherDevice(s.alice) 2929 2930 // Pair alice's two devices 2931 PairDevices(&s.Suite, alicesOtherDevice, s.alice) 2932 PairDevices(&s.Suite, s.alice, alicesOtherDevice) 2933 2934 // Check bob the admin has only zero community 2935 tcs2, err := s.bob.communitiesManager.All() 2936 s.Require().NoError(err, "admin.communitiesManager.All") 2937 s.Len(tcs2, 0, "Must have 0 communities") 2938 2939 // Bob the admin creates a community 2940 createCommunityReq := &requests.CreateCommunity{ 2941 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 2942 Name: "new community", 2943 Color: "#000000", 2944 Description: "new community description", 2945 } 2946 mr, err := s.bob.CreateCommunity(createCommunityReq, true) 2947 s.Require().NoError(err, "CreateCommunity") 2948 s.Require().NotNil(mr) 2949 s.Len(mr.Communities(), 1) 2950 2951 community := mr.Communities()[0] 2952 2953 // Check that admin has 1 community 2954 acs, err := s.bob.communitiesManager.All() 2955 s.Require().NoError(err, "communitiesManager.All") 2956 s.Len(acs, 1, "Must have 1 community") 2957 2958 // Check that Alice has 0 community on either device 2959 cs, err := s.alice.communitiesManager.All() 2960 s.Require().NoError(err, "communitiesManager.All") 2961 s.Len(cs, 0, "Must have 0 communities") 2962 2963 tcs1, err := alicesOtherDevice.communitiesManager.All() 2964 s.Require().NoError(err, "alicesOtherDevice.communitiesManager.All") 2965 s.Len(tcs1, 0, "Must have 0 communities") 2966 2967 // Bob the admin opens up a 1-1 chat with alice 2968 chat := CreateOneToOneChat(common.PubkeyToHex(&s.alice.identity.PublicKey), &s.alice.identity.PublicKey, s.alice.transport) 2969 s.Require().NoError(s.bob.SaveChat(chat)) 2970 2971 // Bob the admin shares with Alice, via public chat, an invite link to the new community 2972 message := buildTestMessage(*chat) 2973 message.CommunityID = community.IDString() 2974 response, err := s.bob.SendChatMessage(context.Background(), message) 2975 s.Require().NoError(err) 2976 s.Require().NotNil(response) 2977 2978 // Retrieve community link & community 2979 err = tt.RetryWithBackOff(func() error { 2980 response, err = s.alice.RetrieveAll() 2981 if err != nil { 2982 return err 2983 } 2984 if len(response.Communities()) == 0 { 2985 return errors.New("no communities received from 1-1") 2986 } 2987 return nil 2988 }) 2989 s.Require().NoError(err) 2990 2991 // Check that alice now has 1 community 2992 cs, err = s.alice.communitiesManager.All() 2993 s.Require().NoError(err, "communitiesManager.All") 2994 s.Len(cs, 1, "Must have 1 community") 2995 for _, c := range cs { 2996 s.False(c.Joined(), "Must not have joined the community") 2997 } 2998 2999 // alice joins the community 3000 mr, err = s.alice.JoinCommunity(context.Background(), community.ID(), false) 3001 s.Require().NoError(err, "s.alice.JoinCommunity") 3002 s.Require().NotNil(mr) 3003 s.Len(mr.Communities(), 1) 3004 aCom := mr.Communities()[0] 3005 3006 // Check that the joined community has the correct values 3007 s.Equal(community.ID(), aCom.ID()) 3008 s.Equal(community.Clock(), aCom.Clock()) 3009 s.Equal(community.PublicKey(), aCom.PublicKey()) 3010 3011 // Check alicesOtherDevice receives the sync join message 3012 err = tt.RetryWithBackOff(func() error { 3013 response, err = alicesOtherDevice.RetrieveAll() 3014 if err != nil { 3015 return err 3016 } 3017 3018 // Do we have a new synced community? 3019 _, err = alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(community.ID()) 3020 if err != nil { 3021 return fmt.Errorf("community with sync not received %w", err) 3022 } 3023 3024 return nil 3025 }) 3026 s.Require().NoError(err) 3027 s.Len(response.Communities(), 1, "") 3028 3029 aoCom := mr.Communities()[0] 3030 s.Equal(aCom, aoCom) 3031 } 3032 3033 func (s *MessengerCommunitiesSuite) TestSyncCommunity_ImportCommunity() { 3034 // Owner creates community 3035 community, _ := s.createCommunity() 3036 s.Require().True(community.IsControlNode()) 3037 3038 // New device is created & paired 3039 ownersOtherDevice := s.createOtherDevice(s.owner) 3040 PairDevices(&s.Suite, ownersOtherDevice, s.owner) 3041 PairDevices(&s.Suite, s.owner, ownersOtherDevice) 3042 3043 privateKey, err := s.owner.ExportCommunity(community.ID()) 3044 s.Require().NoError(err) 3045 3046 // New device imports the community (before it is received via sync message) 3047 ctx := context.Background() 3048 response, err := ownersOtherDevice.ImportCommunity(ctx, privateKey) 3049 s.Require().NoError(err) 3050 s.Require().Len(response.Communities(), 1) 3051 s.Require().Equal(community.IDString(), response.Communities()[0].IDString()) 3052 // New device becomes the control node 3053 s.Require().True(response.Communities()[0].IsControlNode()) 3054 3055 // Old device is no longer the control node 3056 _, err = WaitOnMessengerResponse(s.owner, func(response *MessengerResponse) bool { 3057 if len(response.Communities()) != 1 { 3058 return false 3059 } 3060 c := response.Communities()[0] 3061 return c.IDString() == community.IDString() && !c.IsControlNode() 3062 }, "community not synced") 3063 s.Require().NoError(err) 3064 } 3065 3066 func (s *MessengerCommunitiesSuite) TestSyncCommunity_OutdatedDescription() { 3067 community, _ := s.createCommunity() 3068 s.advertiseCommunityTo(community, s.owner, s.alice) 3069 3070 // Spectate community 3071 _, err := s.alice.SpectateCommunity(community.ID()) 3072 s.Require().NoError(err) 3073 3074 // Update alice's community reference 3075 community, err = s.alice.communitiesManager.GetByID(community.ID()) 3076 s.Require().NoError(err) 3077 s.Require().False(community.Joined()) 3078 s.Require().True(community.Spectated()) 3079 3080 // Create sync message for later 3081 syncCommunityMsg, err := s.alice.buildSyncInstallationCommunity(community, 1) 3082 s.Require().NoError(err) 3083 s.Require().False(syncCommunityMsg.Joined) 3084 s.Require().True(syncCommunityMsg.Spectated) 3085 3086 // Join community 3087 s.joinCommunity(community, s.owner, s.alice) 3088 3089 // Update owner's community reference 3090 community, err = s.owner.GetCommunityByID(community.ID()) 3091 s.Require().NoError(err) 3092 s.Require().True(community.HasMember(s.alice.IdentityPublicKey())) 3093 3094 // Create another device 3095 aliceOtherDevice := s.createOtherDevice(s.alice) 3096 defer TearDownMessenger(&s.Suite, aliceOtherDevice) 3097 3098 // Make other device receive community 3099 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, aliceOtherDevice) 3100 community, err = aliceOtherDevice.GetCommunityByID(community.ID()) 3101 s.Require().NoError(err) 3102 s.Require().True(community.Joined()) 3103 3104 // Then make other device handle sync message with outdated community description 3105 messageState := aliceOtherDevice.buildMessageState() 3106 err = aliceOtherDevice.handleSyncInstallationCommunity(messageState, syncCommunityMsg) 3107 s.Require().NoError(err) 3108 3109 // Then community should not be left 3110 community, err = aliceOtherDevice.communitiesManager.GetByID(community.ID()) 3111 s.Require().NoError(err) 3112 s.Require().True(community.Joined()) 3113 s.Require().False(community.Spectated()) 3114 } 3115 3116 func (s *MessengerCommunitiesSuite) TestSetMutePropertyOnChatsByCategory() { 3117 // Create a community 3118 createCommunityReq := &requests.CreateCommunity{ 3119 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 3120 Name: "new community", 3121 Color: "#000000", 3122 Description: "new community description", 3123 } 3124 3125 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 3126 s.Require().NoError(err, "s.alice.CreateCommunity") 3127 var newCommunity *communities.Community 3128 for _, com := range mr.Communities() { 3129 if com.Name() == createCommunityReq.Name { 3130 newCommunity = com 3131 } 3132 } 3133 s.Require().NotNil(newCommunity) 3134 3135 orgChat1 := &protobuf.CommunityChat{ 3136 Permissions: &protobuf.CommunityPermissions{ 3137 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 3138 }, 3139 Identity: &protobuf.ChatIdentity{ 3140 DisplayName: "status-core", 3141 Emoji: "😎", 3142 Description: "status-core community chat", 3143 }, 3144 } 3145 3146 orgChat2 := &protobuf.CommunityChat{ 3147 Permissions: &protobuf.CommunityPermissions{ 3148 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 3149 }, 3150 Identity: &protobuf.ChatIdentity{ 3151 DisplayName: "status-core2", 3152 Emoji: "😎", 3153 Description: "status-core community chat2", 3154 }, 3155 } 3156 3157 mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat1) 3158 s.Require().NoError(err) 3159 s.Require().NotNil(mr) 3160 s.Require().Len(mr.Communities(), 1) 3161 s.Require().Len(mr.Chats(), 1) 3162 3163 mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat2) 3164 s.Require().NoError(err) 3165 s.Require().NotNil(mr) 3166 s.Require().Len(mr.Communities(), 1) 3167 s.Require().Len(mr.Chats(), 1) 3168 3169 var chatIds []string 3170 for k := range newCommunity.Chats() { 3171 chatIds = append(chatIds, k) 3172 } 3173 category := &requests.CreateCommunityCategory{ 3174 CommunityID: newCommunity.ID(), 3175 CategoryName: "category-name", 3176 ChatIDs: chatIds, 3177 } 3178 3179 mr, err = s.alice.CreateCommunityCategory(category) 3180 s.Require().NoError(err) 3181 s.Require().NotNil(mr) 3182 s.Require().Len(mr.Communities(), 1) 3183 s.Require().Len(mr.Communities()[0].Categories(), 1) 3184 3185 var categoryID string 3186 for k := range mr.Communities()[0].Categories() { 3187 categoryID = k 3188 } 3189 3190 err = s.alice.SetMutePropertyOnChatsByCategory(&requests.MuteCategory{ 3191 CommunityID: newCommunity.IDString(), 3192 CategoryID: categoryID, 3193 MutedType: MuteTillUnmuted, 3194 }, true) 3195 s.Require().NoError(err) 3196 3197 for _, chat := range s.alice.Chats() { 3198 if chat.CategoryID == categoryID { 3199 s.Require().True(chat.Muted) 3200 } 3201 } 3202 3203 err = s.alice.SetMutePropertyOnChatsByCategory(&requests.MuteCategory{ 3204 CommunityID: newCommunity.IDString(), 3205 CategoryID: categoryID, 3206 MutedType: Unmuted, 3207 }, false) 3208 s.Require().NoError(err) 3209 3210 for _, chat := range s.alice.Chats() { 3211 s.Require().False(chat.Muted) 3212 } 3213 } 3214 3215 func (s *MessengerCommunitiesSuite) TestCheckCommunitiesToUnmute() { 3216 // Create a community 3217 createCommunityReq := &requests.CreateCommunity{ 3218 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 3219 Name: "new community", 3220 Color: "#000000", 3221 Description: "new community description", 3222 } 3223 3224 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 3225 s.Require().NoError(err, "s.alice.CreateCommunity") 3226 var newCommunity *communities.Community 3227 for _, com := range mr.Communities() { 3228 if com.Name() == createCommunityReq.Name { 3229 newCommunity = com 3230 } 3231 } 3232 s.Require().NotNil(newCommunity) 3233 3234 currTime, err := time.Parse(time.RFC3339, time.Now().Add(-time.Hour).Format(time.RFC3339)) 3235 s.Require().NoError(err) 3236 3237 err = s.alice.communitiesManager.SetMuted(newCommunity.ID(), true) 3238 s.Require().NoError(err, "SetMuted to community") 3239 3240 err = s.alice.communitiesManager.MuteCommunityTill(newCommunity.ID(), currTime) 3241 s.Require().NoError(err, "SetMuteTill to community") 3242 3243 response, err := s.alice.CheckCommunitiesToUnmute() 3244 s.Require().NoError(err) 3245 s.Require().Len(response.Communities(), 1, "CheckCommunitiesToUnmute should unmute the community") 3246 3247 community, err := s.alice.communitiesManager.GetByID(newCommunity.ID()) 3248 s.Require().NoError(err) 3249 s.Require().False(community.Muted()) 3250 3251 } 3252 3253 func (s *MessengerCommunitiesSuite) TestCommunityNotInDB() { 3254 community, err := s.alice.communitiesManager.GetByID([]byte("0x123")) 3255 s.Require().ErrorIs(err, communities.ErrOrgNotFound) 3256 s.Require().Nil(community) 3257 } 3258 3259 func (s *MessengerCommunitiesSuite) TestMuteAllCommunityChats() { 3260 // Create a community 3261 createCommunityReq := &requests.CreateCommunity{ 3262 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 3263 Name: "new community", 3264 Color: "#000000", 3265 Description: "new community description", 3266 } 3267 3268 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 3269 s.Require().NoError(err, "s.alice.CreateCommunity") 3270 var newCommunity *communities.Community 3271 for _, com := range mr.Communities() { 3272 if com.Name() == createCommunityReq.Name { 3273 newCommunity = com 3274 } 3275 } 3276 s.Require().NotNil(newCommunity) 3277 3278 orgChat1 := &protobuf.CommunityChat{ 3279 Permissions: &protobuf.CommunityPermissions{ 3280 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 3281 }, 3282 Identity: &protobuf.ChatIdentity{ 3283 DisplayName: "status-core", 3284 Emoji: "😎", 3285 Description: "status-core community chat", 3286 }, 3287 } 3288 3289 orgChat2 := &protobuf.CommunityChat{ 3290 Permissions: &protobuf.CommunityPermissions{ 3291 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 3292 }, 3293 Identity: &protobuf.ChatIdentity{ 3294 DisplayName: "status-core2", 3295 Emoji: "😎", 3296 Description: "status-core community chat2", 3297 }, 3298 } 3299 3300 mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat1) 3301 s.Require().NoError(err) 3302 s.Require().NotNil(mr) 3303 s.Require().Len(mr.Communities(), 1) 3304 s.Require().Len(mr.Chats(), 1) 3305 3306 mr, err = s.alice.CreateCommunityChat(newCommunity.ID(), orgChat2) 3307 s.Require().NoError(err) 3308 s.Require().NotNil(mr) 3309 s.Require().Len(mr.Communities(), 1) 3310 s.Require().Len(mr.Chats(), 1) 3311 3312 muteDuration, err := s.alice.MuteDuration(MuteFor15Min) 3313 s.Require().NoError(err) 3314 3315 time, err := s.alice.MuteAllCommunityChats(&requests.MuteCommunity{ 3316 CommunityID: newCommunity.ID(), 3317 MutedType: MuteFor15Min, 3318 }) 3319 s.Require().NoError(err) 3320 s.Require().NotNil(time) 3321 3322 aliceCommunity, err := s.alice.GetCommunityByID(newCommunity.ID()) 3323 s.Require().NoError(err) 3324 s.Require().True(aliceCommunity.Muted()) 3325 3326 for _, chat := range s.alice.Chats() { 3327 if chat.CommunityID == newCommunity.IDString() { 3328 s.Require().True(chat.Muted) 3329 s.Require().Equal(chat.MuteTill, muteDuration) 3330 } 3331 } 3332 3333 for _, chat := range s.alice.Chats() { 3334 if chat.CommunityID == newCommunity.IDString() { 3335 err = s.alice.UnmuteChat(chat.ID) 3336 s.Require().NoError(err) 3337 s.Require().False(chat.Muted) 3338 break 3339 } 3340 } 3341 3342 aliceCommunity, err = s.alice.GetCommunityByID(newCommunity.ID()) 3343 s.Require().NoError(err) 3344 s.Require().False(aliceCommunity.Muted()) 3345 3346 time, err = s.alice.UnMuteAllCommunityChats(newCommunity.IDString()) 3347 s.Require().NoError(err) 3348 s.Require().NotNil(time) 3349 s.Require().False(newCommunity.Muted()) 3350 3351 for _, chat := range s.alice.Chats() { 3352 s.Require().False(chat.Muted) 3353 } 3354 3355 } 3356 3357 func (s *MessengerCommunitiesSuite) TestExtractDiscordChannelsAndCategories() { 3358 3359 tmpFile, err := ioutil.TempFile(os.TempDir(), "discord-channel-") 3360 s.Require().NoError(err) 3361 defer os.Remove(tmpFile.Name()) 3362 3363 discordMessage := &protobuf.DiscordMessage{ 3364 Id: "1234", 3365 Type: "Default", 3366 Timestamp: "2022-07-26T14:20:17.305+00:00", 3367 TimestampEdited: "", 3368 Content: "Some discord message", 3369 Author: &protobuf.DiscordMessageAuthor{ 3370 Id: "123", 3371 Name: "TestAuthor", 3372 Discriminator: "456", 3373 Nickname: "", 3374 AvatarUrl: "", 3375 }, 3376 } 3377 3378 messages := make([]*protobuf.DiscordMessage, 0) 3379 messages = append(messages, discordMessage) 3380 3381 exportedDiscordData := &discord.ExportedData{ 3382 Channel: discord.Channel{ 3383 ID: "12345", 3384 CategoryName: "test-category", 3385 CategoryID: "6789", 3386 Name: "test-channel", 3387 Description: "This is a channel topic", 3388 FilePath: tmpFile.Name(), 3389 }, 3390 Messages: messages, 3391 } 3392 3393 data, err := json.Marshal(exportedDiscordData) 3394 s.Require().NoError(err) 3395 3396 err = os.WriteFile(tmpFile.Name(), data, 0666) // nolint: gosec 3397 s.Require().NoError(err) 3398 3399 files := make([]string, 0) 3400 files = append(files, tmpFile.Name()) 3401 mr, errs := s.bob.ExtractDiscordChannelsAndCategories(files) 3402 s.Require().Len(errs, 0) 3403 3404 s.Require().Len(mr.DiscordCategories, 1) 3405 s.Require().Len(mr.DiscordChannels, 1) 3406 s.Require().Equal(mr.DiscordOldestMessageTimestamp, int(1658845217)) 3407 } 3408 3409 func (s *MessengerCommunitiesSuite) TestExtractDiscordChannelsAndCategories_WithErrors() { 3410 3411 tmpFile, err := ioutil.TempFile(os.TempDir(), "discord-channel-2") 3412 s.Require().NoError(err) 3413 defer os.Remove(tmpFile.Name()) 3414 3415 exportedDiscordData := &discord.ExportedData{ 3416 Channel: discord.Channel{ 3417 ID: "12345", 3418 CategoryName: "test-category", 3419 CategoryID: "6789", 3420 Name: "test-channel", 3421 Description: "This is a channel topic", 3422 FilePath: tmpFile.Name(), 3423 }, 3424 Messages: make([]*protobuf.DiscordMessage, 0), 3425 } 3426 3427 data, err := json.Marshal(exportedDiscordData) 3428 s.Require().NoError(err) 3429 3430 err = os.WriteFile(tmpFile.Name(), data, 0666) // nolint: gosec 3431 s.Require().NoError(err) 3432 3433 files := make([]string, 0) 3434 files = append(files, tmpFile.Name()) 3435 _, errs := s.bob.ExtractDiscordChannelsAndCategories(files) 3436 // Expecting 1 errors since there are no messages to be extracted 3437 s.Require().Len(errs, 1) 3438 } 3439 3440 func (s *MessengerCommunitiesSuite) TestCommunityBanUserRequestToJoin() { 3441 community, _ := s.createCommunity() 3442 3443 s.advertiseCommunityTo(community, s.owner, s.alice) 3444 s.joinCommunity(community, s.owner, s.alice) 3445 3446 response, err := s.owner.BanUserFromCommunity( 3447 context.Background(), 3448 &requests.BanUserFromCommunity{ 3449 CommunityID: community.ID(), 3450 User: common.PubkeyToHexBytes(&s.alice.identity.PublicKey), 3451 }, 3452 ) 3453 s.Require().NoError(err) 3454 s.Require().NotNil(response) 3455 s.Require().Len(response.Communities(), 1) 3456 3457 community = response.Communities()[0] 3458 s.Require().False(community.HasMember(&s.alice.identity.PublicKey)) 3459 s.Require().True(community.IsBanned(&s.alice.identity.PublicKey)) 3460 3461 response, err = WaitOnMessengerResponse( 3462 s.alice, 3463 func(r *MessengerResponse) bool { return len(r.communities) > 0 }, 3464 "no communities", 3465 ) 3466 3467 s.Require().NoError(err) 3468 s.Require().Len(response.Communities(), 1) 3469 3470 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 3471 // We try to join the org 3472 rtj := s.alice.communitiesManager.CreateRequestToJoin(request, s.alice.account.GetCustomizationColor()) 3473 3474 s.Require().NoError(err) 3475 3476 displayName, err := s.alice.settings.DisplayName() 3477 s.Require().NoError(err) 3478 3479 requestToJoinProto := &protobuf.CommunityRequestToJoin{ 3480 Clock: rtj.Clock, 3481 EnsName: rtj.ENSName, 3482 DisplayName: displayName, 3483 CommunityId: community.ID(), 3484 RevealedAccounts: rtj.RevealedAccounts, 3485 } 3486 3487 s.Require().NoError(err) 3488 3489 messageState := s.owner.buildMessageState() 3490 messageState.CurrentMessageState = &CurrentMessageState{} 3491 3492 messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey 3493 3494 statusMessage := v1protocol.StatusMessage{} 3495 statusMessage.TransportLayer.Dst = community.PublicKey() 3496 err = s.owner.HandleCommunityRequestToJoin(messageState, requestToJoinProto, &statusMessage) 3497 3498 s.Require().ErrorContains(err, "can't request access") 3499 } 3500 3501 func (s *MessengerCommunitiesSuite) TestCommunityMaxNumberOfMembers() { 3502 john := s.newMessenger("johnPassword", []string{"0x0765400000000000000000000000000000000000"}) 3503 _, err := john.Start() 3504 s.Require().NoError(err) 3505 3506 defer TearDownMessenger(&s.Suite, john) 3507 3508 // Bring back the original values 3509 defer communities.SetMaxNbMembers(5000) 3510 defer communities.SetMaxNbPendingRequestedMembers(100) 3511 3512 community, _ := s.createCommunity() 3513 3514 communities.SetMaxNbMembers(2) 3515 communities.SetMaxNbPendingRequestedMembers(1) 3516 3517 s.advertiseCommunityTo(community, s.owner, s.alice) 3518 s.advertiseCommunityTo(community, s.owner, s.bob) 3519 s.advertiseCommunityTo(community, s.owner, john) 3520 3521 // Alice joins the community correctly 3522 s.joinCommunity(community, s.owner, s.alice) 3523 3524 // Bob also tries to join, but he will be put in the requests to join to approve and won't join 3525 request := s.createRequestToJoinCommunity(community.ID(), s.bob) 3526 response, err := s.bob.RequestToJoinCommunity(request) 3527 s.Require().NoError(err) 3528 s.Require().NotNil(response) 3529 s.Require().Len(response.RequestsToJoinCommunity(), 1) 3530 requestID := response.RequestsToJoinCommunity()[0].ID 3531 3532 response, err = WaitOnMessengerResponse( 3533 s.owner, 3534 func(r *MessengerResponse) bool { 3535 for _, req := range r.RequestsToJoinCommunity() { 3536 if reflect.DeepEqual(req.ID, requestID) { 3537 return true 3538 } 3539 } 3540 return false 3541 }, 3542 "no request to join", 3543 ) 3544 s.Require().NoError(err) 3545 s.Require().Len(response.RequestsToJoinCommunity(), 1) 3546 s.Require().Equal(communities.RequestToJoinStatePending, response.RequestsToJoinCommunity()[0].State) 3547 3548 // We confirm that there are still 2 members only and the access setting is now manual 3549 updatedCommunity, err := s.owner.communitiesManager.GetByID(community.ID()) 3550 s.Require().NoError(err) 3551 s.Require().Len(updatedCommunity.Members(), 2) 3552 s.Require().Equal(protobuf.CommunityPermissions_MANUAL_ACCEPT, updatedCommunity.Permissions().Access) 3553 3554 // John also tries to join, but he his request will be ignored as it exceeds the max number of pending requests 3555 requestJohn := s.createRequestToJoinCommunity(community.ID(), john) 3556 response, err = john.RequestToJoinCommunity(requestJohn) 3557 s.Require().NoError(err) 3558 s.Require().NotNil(response) 3559 s.Require().Len(response.RequestsToJoinCommunity(), 1) 3560 requestJohnID := response.RequestsToJoinCommunity()[0].ID 3561 3562 _, err = WaitOnMessengerResponse( 3563 s.owner, 3564 func(r *MessengerResponse) bool { 3565 for _, req := range r.RequestsToJoinCommunity() { 3566 if reflect.DeepEqual(req.ID, requestJohnID) { 3567 return true 3568 } 3569 } 3570 return false 3571 }, 3572 "no request to join", 3573 ) 3574 s.Require().Error(err) 3575 } 3576 3577 func (s *MessengerCommunitiesSuite) TestHandleImport() { 3578 community, chat := s.createCommunity() 3579 3580 s.advertiseCommunityTo(community, s.owner, s.alice) 3581 s.joinCommunity(community, s.owner, s.alice) 3582 3583 // Check that there are no messages in the chat at first 3584 chat, err := s.alice.persistence.Chat(chat.ID) 3585 s.Require().NoError(err) 3586 s.Require().NotNil(chat) 3587 s.Require().Equal(0, int(chat.UnviewedMessagesCount)) 3588 3589 // Create an message that will be imported 3590 testMessage := protobuf.ChatMessage{ 3591 Text: "abc123", 3592 ChatId: chat.ID, 3593 ContentType: protobuf.ChatMessage_TEXT_PLAIN, 3594 MessageType: protobuf.MessageType_COMMUNITY_CHAT, 3595 Clock: 1, 3596 Timestamp: 1, 3597 } 3598 encodedPayload, err := proto.Marshal(&testMessage) 3599 s.Require().NoError(err) 3600 wrappedPayload, err := v1protocol.WrapMessageV1( 3601 encodedPayload, 3602 protobuf.ApplicationMetadataMessage_CHAT_MESSAGE, 3603 s.owner.identity, 3604 ) 3605 s.Require().NoError(err) 3606 3607 message := &types.Message{} 3608 message.Sig = crypto.FromECDSAPub(&s.owner.identity.PublicKey) 3609 message.Payload = wrappedPayload 3610 3611 filter := s.alice.transport.FilterByChatID(chat.ID) 3612 importedMessages := make(map[transport.Filter][]*types.Message, 0) 3613 3614 importedMessages[*filter] = append(importedMessages[*filter], message) 3615 3616 // Import that message 3617 err = s.alice.handleImportedMessages(importedMessages) 3618 s.Require().NoError(err) 3619 3620 // Get the chat again and see that there is still no unread message because we don't count import messages 3621 chat, err = s.alice.persistence.Chat(chat.ID) 3622 s.Require().NoError(err) 3623 s.Require().NotNil(chat) 3624 s.Require().Equal(0, int(chat.UnviewedMessagesCount)) 3625 } 3626 3627 func (s *MessengerCommunitiesSuite) TestGetCommunityIdFromKey() { 3628 publicKey := "0x029e4777ce55f20373db33546c8681a082bd181d665c87e18d4306766de9302b53" 3629 privateKey := "0x3f932031cb5f94ba7eb8ab4c824c3677973ab01fde65d1b89e0b3f470003a2cd" 3630 3631 // Public key returns the same 3632 communityID := GetCommunityIDFromKey(publicKey) 3633 s.Require().Equal(communityID, publicKey) 3634 3635 // Private key returns the public key 3636 communityID = GetCommunityIDFromKey(privateKey) 3637 s.Require().Equal(communityID, publicKey) 3638 } 3639 3640 type testPermissionChecker struct { 3641 } 3642 3643 func (t *testPermissionChecker) CheckPermissionToJoin(*communities.Community, []gethcommon.Address) (*communities.CheckPermissionToJoinResponse, error) { 3644 return &communities.CheckPermissionsResponse{Satisfied: true}, nil 3645 3646 } 3647 3648 func (t *testPermissionChecker) CheckPermissions(permissionsParsedData *communities.PreParsedCommunityPermissionsData, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool) (*communities.CheckPermissionsResponse, error) { 3649 return &communities.CheckPermissionsResponse{Satisfied: true}, nil 3650 } 3651 3652 func (t *testPermissionChecker) CheckPermissionsWithPreFetchedData(permissionsParsedData *communities.PreParsedCommunityPermissionsData, accountsAndChainIDs []*communities.AccountChainIDsCombination, shortcircuit bool, collectiblesOwners communities.CollectiblesOwners) (*communities.CheckPermissionsResponse, error) { 3653 return &communities.CheckPermissionsResponse{Satisfied: true}, nil 3654 } 3655 3656 func (s *MessengerCommunitiesSuite) TestStartCommunityRekeyLoop() { 3657 community, chat := createEncryptedCommunity(&s.Suite, s.owner) 3658 s.Require().True(community.Encrypted()) 3659 s.Require().True(community.ChannelEncrypted(chat.CommunityChatID())) 3660 3661 s.mockPermissionCheckerForAllMessenger() 3662 3663 s.advertiseCommunityTo(community, s.owner, s.bob) 3664 s.advertiseCommunityTo(community, s.owner, s.alice) 3665 s.joinCommunity(community, s.owner, s.bob) 3666 s.joinCommunity(community, s.owner, s.alice) 3667 3668 // Check keys in the database 3669 communityKeys, err := s.owner.sender.GetKeysForGroup(community.ID()) 3670 s.Require().NoError(err) 3671 communityKeyCount := len(communityKeys) 3672 3673 channelKeys, err := s.owner.sender.GetKeysForGroup([]byte(chat.ID)) 3674 s.Require().NoError(err) 3675 channelKeyCount := len(channelKeys) 3676 3677 // Check that rekeying is occurring by counting the number of keyIDs in the encryptor's DB 3678 // This test could be flaky, as the rekey function may not be finished before RekeyInterval * 2 has passed 3679 for i := 0; i < 5; i++ { 3680 time.Sleep(s.owner.communitiesManager.RekeyInterval * 2) 3681 communityKeys, err = s.owner.sender.GetKeysForGroup(community.ID()) 3682 s.Require().NoError(err) 3683 s.Require().Greater(len(communityKeys), communityKeyCount) 3684 communityKeyCount = len(communityKeys) 3685 3686 channelKeys, err = s.owner.sender.GetKeysForGroup([]byte(chat.ID)) 3687 s.Require().NoError(err) 3688 s.Require().Greater(len(channelKeys), channelKeyCount) 3689 channelKeyCount = len(channelKeys) 3690 } 3691 } 3692 3693 func (s *MessengerCommunitiesSuite) TestCommunityRekeyAfterBan() { 3694 s.owner.communitiesManager.RekeyInterval = 500 * time.Minute 3695 3696 // Create a new community 3697 response, err := s.owner.CreateCommunity( 3698 &requests.CreateCommunity{ 3699 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 3700 Name: "status", 3701 Color: "#57a7e5", 3702 Description: "status community description", 3703 }, 3704 true, 3705 ) 3706 s.Require().NoError(err) 3707 s.Require().NotNil(response) 3708 s.Require().Len(response.Communities(), 1) 3709 s.Require().Len(response.Communities()[0].Members(), 1) 3710 3711 // Check community is present in the DB and has default values we care about 3712 c, err := s.owner.GetCommunityByID(response.Communities()[0].ID()) 3713 s.Require().NoError(err) 3714 s.Require().False(c.Encrypted()) 3715 // TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix()) 3716 3717 _, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ 3718 CommunityID: c.ID(), 3719 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 3720 TokenCriteria: []*protobuf.TokenCriteria{{ 3721 ContractAddresses: map[uint64]string{3: "0x933"}, 3722 Type: protobuf.CommunityTokenType_ERC20, 3723 Symbol: "STT", 3724 Name: "Status Test Token", 3725 AmountInWei: "10000000000000000000", 3726 Decimals: 18, 3727 }}, 3728 }) 3729 s.Require().NoError(err) 3730 3731 c, err = s.owner.GetCommunityByID(c.ID()) 3732 s.Require().NoError(err) 3733 s.Require().True(c.Encrypted()) 3734 3735 s.advertiseCommunityTo(c, s.owner, s.bob) 3736 s.advertiseCommunityTo(c, s.owner, s.alice) 3737 3738 s.mockPermissionCheckerForAllMessenger() 3739 3740 s.joinCommunity(c, s.owner, s.bob) 3741 s.joinCommunity(c, s.owner, s.alice) 3742 3743 // Check the Alice and Bob are members of the community 3744 c, err = s.owner.GetCommunityByID(c.ID()) 3745 s.Require().NoError(err) 3746 s.Require().True(c.HasMember(&s.alice.identity.PublicKey)) 3747 s.Require().True(c.HasMember(&s.bob.identity.PublicKey)) 3748 3749 // Make sure at least one key makes it to alice 3750 response, err = WaitOnMessengerResponse(s.alice, 3751 func(r *MessengerResponse) bool { 3752 keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID()) 3753 if err != nil || len(keys) != 1 { 3754 return false 3755 } 3756 return true 3757 3758 }, 3759 "alice does not have enough keys", 3760 ) 3761 s.Require().NoError(err) 3762 3763 response, err = s.owner.BanUserFromCommunity(context.Background(), &requests.BanUserFromCommunity{ 3764 CommunityID: c.ID(), 3765 User: common.PubkeyToHexBytes(&s.bob.identity.PublicKey), 3766 }) 3767 s.Require().NoError(err) 3768 s.Require().Len(response.Communities(), 1) 3769 3770 s.Require().False(response.Communities()[0].HasMember(&s.bob.identity.PublicKey)) 3771 3772 // Check bob has been banned 3773 response, err = WaitOnMessengerResponse(s.alice, 3774 func(r *MessengerResponse) bool { 3775 return len(r.Communities()) == 1 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey) 3776 3777 }, 3778 "alice didn't receive updated description", 3779 ) 3780 s.Require().NoError(err) 3781 3782 response, err = WaitOnMessengerResponse(s.alice, 3783 func(r *MessengerResponse) bool { 3784 keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID()) 3785 if err != nil || len(keys) < 2 { 3786 return false 3787 } 3788 return true 3789 3790 }, 3791 "alice hasn't received updated key", 3792 ) 3793 s.Require().NoError(err) 3794 } 3795 3796 func (s *MessengerCommunitiesSuite) TestCommunityRekeyAfterBanDisableCompatibility() { 3797 common.RekeyCompatibility = false 3798 s.owner.communitiesManager.RekeyInterval = 500 * time.Minute 3799 3800 // Create a new community 3801 response, err := s.owner.CreateCommunity( 3802 &requests.CreateCommunity{ 3803 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 3804 Name: "status", 3805 Color: "#57a7e5", 3806 Description: "status community description", 3807 }, 3808 true, 3809 ) 3810 s.Require().NoError(err) 3811 s.Require().NotNil(response) 3812 s.Require().Len(response.Communities(), 1) 3813 3814 // Check community is present in the DB and has default values we care about 3815 c, err := s.owner.GetCommunityByID(response.Communities()[0].ID()) 3816 s.Require().NoError(err) 3817 s.Require().False(c.Encrypted()) 3818 // TODO some check that there are no keys for the community. Alt for s.Require().Zero(c.RekeyedAt().Unix()) 3819 3820 _, err = s.owner.CreateCommunityTokenPermission(&requests.CreateCommunityTokenPermission{ 3821 CommunityID: c.ID(), 3822 Type: protobuf.CommunityTokenPermission_BECOME_MEMBER, 3823 TokenCriteria: []*protobuf.TokenCriteria{{ 3824 ContractAddresses: map[uint64]string{3: "0x933"}, 3825 Type: protobuf.CommunityTokenType_ERC20, 3826 Symbol: "STT", 3827 Name: "Status Test Token", 3828 AmountInWei: "10000000000000000000", 3829 Decimals: 18, 3830 }}, 3831 }) 3832 s.Require().NoError(err) 3833 3834 c, err = s.owner.GetCommunityByID(c.ID()) 3835 s.Require().NoError(err) 3836 s.Require().True(c.Encrypted()) 3837 3838 s.advertiseCommunityTo(c, s.owner, s.bob) 3839 s.advertiseCommunityTo(c, s.owner, s.alice) 3840 3841 s.mockPermissionCheckerForAllMessenger() 3842 3843 s.joinCommunity(c, s.owner, s.bob) 3844 s.joinCommunity(c, s.owner, s.alice) 3845 3846 // Check the Alice and Bob are members of the community 3847 c, err = s.owner.GetCommunityByID(c.ID()) 3848 s.Require().NoError(err) 3849 s.Require().True(c.HasMember(&s.alice.identity.PublicKey)) 3850 s.Require().True(c.HasMember(&s.bob.identity.PublicKey)) 3851 3852 // Make sure at least one key makes it to alice 3853 response, err = WaitOnMessengerResponse(s.alice, 3854 func(r *MessengerResponse) bool { 3855 keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID()) 3856 if err != nil || len(keys) != 1 { 3857 return false 3858 } 3859 return true 3860 3861 }, 3862 "alice does not have enough keys", 3863 ) 3864 s.Require().NoError(err) 3865 3866 response, err = s.owner.BanUserFromCommunity(context.Background(), &requests.BanUserFromCommunity{ 3867 CommunityID: c.ID(), 3868 User: common.PubkeyToHexBytes(&s.bob.identity.PublicKey), 3869 }) 3870 s.Require().NoError(err) 3871 s.Require().Len(response.Communities(), 1) 3872 3873 s.Require().False(response.Communities()[0].HasMember(&s.bob.identity.PublicKey)) 3874 3875 // Check bob has been banned 3876 response, err = WaitOnMessengerResponse(s.alice, 3877 func(r *MessengerResponse) bool { 3878 return len(r.Communities()) == 1 && !r.Communities()[0].HasMember(&s.bob.identity.PublicKey) 3879 3880 }, 3881 "alice didn't receive updated description", 3882 ) 3883 s.Require().NoError(err) 3884 3885 response, err = WaitOnMessengerResponse(s.alice, 3886 func(r *MessengerResponse) bool { 3887 keys, err := s.alice.encryptor.GetKeysForGroup(response.Communities()[0].ID()) 3888 if err != nil || len(keys) < 2 { 3889 return false 3890 } 3891 return true 3892 3893 }, 3894 "alice hasn't received updated key", 3895 ) 3896 s.Require().NoError(err) 3897 } 3898 3899 func (s *MessengerCommunitiesSuite) TestRetrieveBigCommunity() { 3900 bigEmoji := make([]byte, 4*1024*1024) // 4 MB 3901 description := &requests.CreateCommunity{ 3902 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 3903 Name: "status", 3904 Color: "#ffffff", 3905 Description: "status community description", 3906 Emoji: string(bigEmoji), 3907 } 3908 3909 // checks that private messages are segmented 3910 // (community is advertised through `SendPrivate`) 3911 response, err := s.owner.CreateCommunity(description, true) 3912 s.Require().NoError(err) 3913 s.Require().NotNil(response) 3914 s.Require().Len(response.Communities(), 1) 3915 community := response.Communities()[0] 3916 3917 s.advertiseCommunityTo(community, s.owner, s.alice) 3918 s.joinCommunity(community, s.owner, s.alice) 3919 3920 // checks that public messages are segmented 3921 // (community is advertised through `SendPublic`) 3922 updatedDescription := "status updated community description" 3923 _, err = s.owner.EditCommunity(&requests.EditCommunity{ 3924 CommunityID: community.ID(), 3925 CreateCommunity: requests.CreateCommunity{ 3926 Membership: protobuf.CommunityPermissions_AUTO_ACCEPT, 3927 Name: "status", 3928 Color: "#ffffff", 3929 Description: updatedDescription, 3930 Emoji: string(bigEmoji), 3931 }, 3932 }) 3933 s.Require().NoError(err) 3934 3935 // alice receives updated description 3936 _, err = WaitOnMessengerResponse(s.alice, func(r *MessengerResponse) bool { 3937 return len(r.Communities()) > 0 && r.Communities()[0].DescriptionText() == updatedDescription 3938 }, "updated description not received") 3939 s.Require().NoError(err) 3940 } 3941 3942 func (s *MessengerCommunitiesSuite) TestRequestAndCancelCommunityAdminOffline() { 3943 ctx := context.Background() 3944 3945 community, _ := s.createCommunity() 3946 s.advertiseCommunityTo(community, s.owner, s.alice) 3947 3948 request := s.createRequestToJoinCommunity(community.ID(), s.alice) 3949 // We try to join the org 3950 response, err := s.alice.RequestToJoinCommunity(request) 3951 s.Require().NoError(err) 3952 s.Require().NotNil(response) 3953 s.Require().Len(response.RequestsToJoinCommunity(), 1) 3954 3955 requestToJoin1 := response.RequestsToJoinCommunity()[0] 3956 s.Require().NotNil(requestToJoin1) 3957 s.Require().Equal(community.ID(), requestToJoin1.CommunityID) 3958 s.Require().True(requestToJoin1.Our) 3959 s.Require().NotEmpty(requestToJoin1.ID) 3960 s.Require().NotEmpty(requestToJoin1.Clock) 3961 s.Require().Equal(requestToJoin1.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 3962 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State) 3963 3964 messageState := s.alice.buildMessageState() 3965 messageState.CurrentMessageState = &CurrentMessageState{} 3966 3967 messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey 3968 3969 statusMessage := v1protocol.StatusMessage{} 3970 statusMessage.TransportLayer.Dst = community.PublicKey() 3971 3972 requestToJoinProto := &protobuf.CommunityRequestToJoin{ 3973 Clock: requestToJoin1.Clock, 3974 EnsName: requestToJoin1.ENSName, 3975 DisplayName: "Alice", 3976 CommunityId: community.ID(), 3977 RevealedAccounts: requestToJoin1.RevealedAccounts, 3978 } 3979 3980 err = s.owner.HandleCommunityRequestToJoin(messageState, requestToJoinProto, &statusMessage) 3981 s.Require().NoError(err) 3982 ownerCommunity, err := s.owner.GetCommunityByID(community.ID()) 3983 // Check Alice has successfully joined at owner side, Because message order was correct 3984 s.Require().True(ownerCommunity.HasMember(s.alice.IdentityPublicKey())) 3985 s.Require().NoError(err) 3986 3987 s.Require().Len(response.Communities(), 1) 3988 s.Require().Equal(response.Communities()[0].RequestedToJoinAt(), requestToJoin1.Clock) 3989 3990 // pull all communities to make sure we set RequestedToJoinAt 3991 3992 allCommunities, err := s.alice.Communities() 3993 s.Require().NoError(err) 3994 s.Require().Len(allCommunities, 1) 3995 s.Require().Equal(allCommunities[0].ID(), community.ID()) 3996 s.Require().Equal(allCommunities[0].RequestedToJoinAt(), requestToJoin1.Clock) 3997 3998 // pull to make sure it has been saved 3999 requestsToJoin, err := s.alice.MyPendingRequestsToJoin() 4000 s.Require().NoError(err) 4001 s.Require().Len(requestsToJoin, 1) 4002 4003 // Make sure the requests are fetched also by community 4004 requestsToJoin, err = s.alice.PendingRequestsToJoinForCommunity(community.ID()) 4005 s.Require().NoError(err) 4006 s.Require().Len(requestsToJoin, 1) 4007 4008 requestToJoin2 := response.RequestsToJoinCommunity()[0] 4009 4010 s.Require().NotNil(requestToJoin2) 4011 s.Require().Equal(community.ID(), requestToJoin2.CommunityID) 4012 s.Require().NotEmpty(requestToJoin2.ID) 4013 s.Require().NotEmpty(requestToJoin2.Clock) 4014 s.Require().Equal(requestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 4015 s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin2.State) 4016 4017 s.Require().Equal(requestToJoin1.ID, requestToJoin2.ID) 4018 4019 requestToCancel := &requests.CancelRequestToJoinCommunity{ID: requestToJoin1.ID} 4020 response, err = s.alice.CancelRequestToJoinCommunity(ctx, requestToCancel) 4021 s.Require().NoError(err) 4022 s.Require().NotNil(response) 4023 s.Require().Len(response.RequestsToJoinCommunity(), 1) 4024 s.Require().Equal(communities.RequestToJoinStateCanceled, response.RequestsToJoinCommunity()[0].State) 4025 4026 messageState = s.alice.buildMessageState() 4027 messageState.CurrentMessageState = &CurrentMessageState{} 4028 4029 messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey 4030 4031 statusMessage.TransportLayer.Dst = community.PublicKey() 4032 4033 requestToJoinCancelProto := &protobuf.CommunityRequestToJoinResponse{ 4034 CommunityId: community.ID(), 4035 Clock: requestToJoin1.Clock + 1, 4036 Accepted: true, 4037 } 4038 4039 err = s.alice.HandleCommunityRequestToJoinResponse(messageState, requestToJoinCancelProto, &statusMessage) 4040 s.Require().NoError(err) 4041 aliceJoinedCommunities, err := s.alice.JoinedCommunities() 4042 s.Require().NoError(err) 4043 // Make sure on Alice side she hasn't joined any communities 4044 s.Require().Empty(aliceJoinedCommunities) 4045 4046 // pull to make sure it has been saved 4047 myRequestToJoinId := communities.CalculateRequestID(s.alice.IdentityPublicKeyString(), community.ID()) 4048 canceledRequestToJoin, err := s.alice.communitiesManager.GetRequestToJoin(myRequestToJoinId) 4049 s.Require().NoError(err) 4050 s.Require().NotNil(canceledRequestToJoin) 4051 s.Require().Equal(canceledRequestToJoin.State, communities.RequestToJoinStateCanceled) 4052 4053 s.Require().NoError(err) 4054 4055 messageState = s.alice.buildMessageState() 4056 messageState.CurrentMessageState = &CurrentMessageState{} 4057 4058 messageState.CurrentMessageState.PublicKey = &s.alice.identity.PublicKey 4059 4060 statusMessage.TransportLayer.Dst = community.PublicKey() 4061 4062 requestToJoinResponseProto := &protobuf.CommunityRequestToJoinResponse{ 4063 Clock: canceledRequestToJoin.Clock, 4064 CommunityId: community.ID(), 4065 Accepted: true, 4066 } 4067 4068 err = s.alice.HandleCommunityRequestToJoinResponse(messageState, requestToJoinResponseProto, &statusMessage) 4069 s.Require().NoError(err) 4070 // Make sure alice is NOT a member of the community that she cancelled her request to join to 4071 s.Require().False(community.HasMember(s.alice.IdentityPublicKey())) 4072 // Make sure there are no AC notifications for Alice 4073 aliceNotifications, err := s.alice.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 4074 Cursor: "", 4075 Limit: 10, 4076 ActivityTypes: []ActivityCenterType{}, 4077 ReadType: ActivityCenterQueryParamsReadUnread, 4078 }) 4079 s.Require().NoError(err) 4080 s.Require().Len(aliceNotifications.Notifications, 0) 4081 4082 // Retrieve activity center notifications for admin to make sure the request notification is deleted 4083 notifications, err := s.owner.ActivityCenterNotifications(ActivityCenterNotificationsRequest{ 4084 Cursor: "", 4085 Limit: 10, 4086 ActivityTypes: []ActivityCenterType{}, 4087 ReadType: ActivityCenterQueryParamsReadUnread, 4088 }) 4089 4090 s.Require().NoError(err) 4091 s.Require().Len(notifications.Notifications, 0) 4092 cancelRequestToJoin2 := response.RequestsToJoinCommunity()[0] 4093 s.Require().NotNil(cancelRequestToJoin2) 4094 s.Require().Equal(community.ID(), cancelRequestToJoin2.CommunityID) 4095 s.Require().False(cancelRequestToJoin2.Our) 4096 s.Require().NotEmpty(cancelRequestToJoin2.ID) 4097 s.Require().NotEmpty(cancelRequestToJoin2.Clock) 4098 s.Require().Equal(cancelRequestToJoin2.PublicKey, common.PubkeyToHex(&s.alice.identity.PublicKey)) 4099 } 4100 4101 func (s *MessengerCommunitiesSuite) TestCommunityLastOpenedAt() { 4102 community, _ := s.createCommunity() 4103 s.advertiseCommunityTo(community, s.owner, s.alice) 4104 s.joinCommunity(community, s.owner, s.alice) 4105 4106 // Mock frontend triggering communityUpdateLastOpenedAt 4107 lastOpenedAt1, err := s.alice.CommunityUpdateLastOpenedAt(community.IDString()) 4108 s.Require().NoError(err) 4109 4110 // Check lastOpenedAt was updated 4111 s.Require().True(lastOpenedAt1 > 0) 4112 4113 // Nap for a bit 4114 time.Sleep(time.Second) 4115 4116 // Check lastOpenedAt was successfully updated twice 4117 lastOpenedAt2, err := s.alice.CommunityUpdateLastOpenedAt(community.IDString()) 4118 s.Require().NoError(err) 4119 4120 s.Require().True(lastOpenedAt2 > lastOpenedAt1) 4121 } 4122 4123 func (s *MessengerCommunitiesSuite) TestSyncCommunityLastOpenedAt() { 4124 // Create new device 4125 alicesOtherDevice := s.createOtherDevice(s.alice) 4126 PairDevices(&s.Suite, alicesOtherDevice, s.alice) 4127 4128 // Create a community 4129 createCommunityReq := &requests.CreateCommunity{ 4130 Membership: protobuf.CommunityPermissions_MANUAL_ACCEPT, 4131 Name: "new community", 4132 Color: "#000000", 4133 Description: "new community description", 4134 } 4135 4136 mr, err := s.alice.CreateCommunity(createCommunityReq, true) 4137 s.Require().NoError(err, "s.alice.CreateCommunity") 4138 var newCommunity *communities.Community 4139 for _, com := range mr.Communities() { 4140 if com.Name() == createCommunityReq.Name { 4141 newCommunity = com 4142 } 4143 } 4144 s.Require().NotNil(newCommunity) 4145 4146 // Mock frontend triggering communityUpdateLastOpenedAt 4147 lastOpenedAt, err := s.alice.CommunityUpdateLastOpenedAt(newCommunity.IDString()) 4148 s.Require().NoError(err) 4149 4150 // Check lastOpenedAt was updated 4151 s.Require().True(lastOpenedAt > 0) 4152 4153 err = tt.RetryWithBackOff(func() error { 4154 _, err = alicesOtherDevice.RetrieveAll() 4155 if err != nil { 4156 return err 4157 } 4158 // Do we have a new synced community? 4159 _, err := alicesOtherDevice.communitiesManager.GetSyncedRawCommunity(newCommunity.ID()) 4160 if err != nil { 4161 return fmt.Errorf("community with sync not received %w", err) 4162 } 4163 4164 return nil 4165 }) 4166 otherDeviceCommunity, err := alicesOtherDevice.communitiesManager.GetByID(newCommunity.ID()) 4167 s.Require().NoError(err) 4168 s.Require().True(otherDeviceCommunity.LastOpenedAt() > 0) 4169 } 4170 4171 func (s *MessengerCommunitiesSuite) TestBanUserAndDeleteAllUserMessages() { 4172 community, _ := s.createCommunity() 4173 4174 orgChat := &protobuf.CommunityChat{ 4175 Permissions: &protobuf.CommunityPermissions{ 4176 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 4177 }, 4178 Identity: &protobuf.ChatIdentity{ 4179 DisplayName: "chat test delete messages", 4180 Emoji: "😎", 4181 Description: "status-core community chat", 4182 }, 4183 } 4184 response, err := s.owner.CreateCommunityChat(community.ID(), orgChat) 4185 s.Require().NoError(err) 4186 s.Require().NotNil(response) 4187 s.Require().Len(response.Communities(), 1) 4188 s.Require().Len(response.Chats(), 1) 4189 4190 community = response.Communities()[0] 4191 communityChat := response.Chats()[0] 4192 4193 s.advertiseCommunityTo(community, s.owner, s.alice) 4194 s.joinCommunity(community, s.owner, s.alice) 4195 4196 inputMessage := buildTestMessage(*communityChat) 4197 4198 sendResponse, err := s.alice.SendChatMessage(context.Background(), inputMessage) 4199 s.NoError(err) 4200 s.Require().NotNil(sendResponse) 4201 s.Require().Len(sendResponse.Messages(), 1) 4202 messageID := sendResponse.Messages()[0].ID 4203 4204 response, err = WaitOnMessengerResponse( 4205 s.owner, 4206 func(r *MessengerResponse) bool { 4207 if len(r.Messages()) == 0 { 4208 return false 4209 } 4210 4211 for _, message := range r.Messages() { 4212 if message.ID == messageID { 4213 return true 4214 } 4215 } 4216 return false 4217 }, 4218 "no messages", 4219 ) 4220 s.Require().NoError(err) 4221 s.Require().Len(response.Messages(), 1) 4222 s.Require().Equal(messageID, response.Messages()[0].ID) 4223 4224 response, err = s.owner.BanUserFromCommunity( 4225 context.Background(), 4226 &requests.BanUserFromCommunity{ 4227 CommunityID: community.ID(), 4228 User: common.PubkeyToHexBytes(&s.alice.identity.PublicKey), 4229 DeleteAllMessages: true, 4230 }, 4231 ) 4232 4233 s.Require().NoError(err) 4234 s.Require().NotNil(response) 4235 s.Require().Len(response.Communities(), 1) 4236 s.Require().Len(response.Messages(), 0) 4237 s.Require().Len(response.RemovedMessages(), 0) 4238 s.Require().Len(response.DeletedMessages(), 1) 4239 // we are removing last message, so we must get chat update too 4240 4241 community = response.Communities()[0] 4242 s.Require().False(community.HasMember(&s.alice.identity.PublicKey)) 4243 s.Require().True(community.IsBanned(&s.alice.identity.PublicKey)) 4244 s.Require().Len(community.PendingAndBannedMembers(), 1) 4245 s.Require().Equal(community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()], communities.CommunityMemberBanWithAllMessagesDelete) 4246 4247 response, err = WaitOnMessengerResponse( 4248 s.alice, 4249 func(r *MessengerResponse) bool { 4250 return r != nil && len(r.DeletedMessages()) > 0 4251 }, 4252 "no removed message for alice", 4253 ) 4254 4255 s.Require().NoError(err) 4256 s.Require().NotNil(response) 4257 s.Require().Len(response.Communities(), 1) 4258 s.Require().Len(response.Messages(), 0) 4259 s.Require().Len(response.ActivityCenterNotifications(), 1) 4260 s.Require().Len(response.RemovedMessages(), 0) 4261 s.Require().Len(response.DeletedMessages(), 1) 4262 // we are removing last message, so we must get chat update too 4263 4264 community = response.Communities()[0] 4265 s.Require().False(community.HasMember(&s.alice.identity.PublicKey)) 4266 s.Require().True(community.IsBanned(&s.alice.identity.PublicKey)) 4267 s.Require().Len(community.PendingAndBannedMembers(), 1) 4268 s.Require().Equal(community.PendingAndBannedMembers()[s.alice.IdentityPublicKeyString()], communities.CommunityMemberBanWithAllMessagesDelete) 4269 s.Require().False(community.Joined()) 4270 s.Require().False(community.Spectated()) 4271 } 4272 4273 func (s *MessengerCommunitiesSuite) TestIsDisplayNameDupeOfCommunityMember() { 4274 community, _ := s.createCommunity() 4275 advertiseCommunityToUserOldWay(&s.Suite, community, s.owner, s.alice) 4276 4277 s.joinCommunity(community, s.owner, s.alice) 4278 4279 result, err := s.alice.IsDisplayNameDupeOfCommunityMember("Charlie") 4280 s.Require().NoError(err) 4281 s.Require().True(result) 4282 4283 result, err = s.alice.IsDisplayNameDupeOfCommunityMember("Alice") 4284 s.Require().NoError(err) 4285 s.Require().True(result) 4286 4287 result, err = s.alice.IsDisplayNameDupeOfCommunityMember("Bobby") 4288 s.Require().NoError(err) 4289 s.Require().False(result) 4290 } 4291 4292 func (s *MessengerCommunitiesSuite) sendImageToCommunity(sender *Messenger, chatID string) *common.Message { 4293 ctx := context.Background() 4294 messageToSend := common.NewMessage() 4295 messageToSend.ChatId = chatID 4296 messageToSend.ContentType = protobuf.ChatMessage_IMAGE 4297 4298 // base64 image 4299 encodedB64Image := "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII" 4300 decodedBytes, _ := base64.StdEncoding.DecodeString(encodedB64Image) 4301 4302 messageToSend.Payload = &protobuf.ChatMessage_Image{ 4303 Image: &protobuf.ImageMessage{ 4304 Format: 1, // PNG 4305 Payload: decodedBytes, 4306 }, 4307 } 4308 4309 response, err := sender.SendChatMessage(ctx, messageToSend) 4310 s.Require().NoError(err) 4311 s.Require().Len(response.Messages(), 1) 4312 sentMessage := response.Messages()[0] 4313 4314 receivers := []*Messenger{s.alice, s.bob, s.owner} 4315 for _, receiver := range receivers { 4316 if receiver == sender { 4317 continue 4318 } 4319 _, err = WaitOnMessengerResponse(receiver, func(response *MessengerResponse) bool { 4320 return len(response.Messages()) == 1 && response.Messages()[0].ID == sentMessage.ID 4321 }, "receiver did not receive message") 4322 s.Require().NoError(err) 4323 } 4324 return sentMessage 4325 } 4326 4327 func (s *MessengerCommunitiesSuite) TestSerializedCommunities() { 4328 addMediaServer := func(messenger *Messenger) { 4329 mediaServer, err := server.NewMediaServer(messenger.database, nil, nil, nil) 4330 s.Require().NoError(err) 4331 s.Require().NoError(mediaServer.Start()) 4332 messenger.SetMediaServer(mediaServer) 4333 } 4334 addMediaServer(s.owner) 4335 4336 community, _ := s.createCommunity() 4337 // update community description 4338 description := community.Description() 4339 identImageName := "small" 4340 identImagePayload := []byte("123") 4341 description.Identity = &protobuf.ChatIdentity{ 4342 Images: map[string]*protobuf.IdentityImage{ 4343 identImageName: { 4344 Payload: identImagePayload, 4345 }, 4346 }, 4347 } 4348 // #nosec G101 4349 tokenImageInBase64 := "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/wcAAwAB/tdgBUgAAAAASUVORK5CYII=" 4350 description.CommunityTokensMetadata = []*protobuf.CommunityTokenMetadata{ 4351 { 4352 Image: tokenImageInBase64, 4353 Symbol: "STT", 4354 }, 4355 } 4356 description.Clock = description.Clock + 1 4357 community.Edit(description) 4358 s.Require().NoError(s.owner.communitiesManager.SaveCommunity(community)) 4359 4360 // check edit was successful 4361 b, err := s.owner.communitiesManager.GetByID(community.ID()) 4362 s.Require().NoError(err) 4363 s.Require().NotNil(b) 4364 s.Len(b.Description().CommunityTokensMetadata, 1) 4365 s.Equal(tokenImageInBase64, b.Description().CommunityTokensMetadata[0].Image) 4366 s.Len(b.Description().Identity.Images, 1) 4367 s.Equal(identImagePayload, b.Description().Identity.Images[identImageName].Payload) 4368 4369 c, err := s.owner.Communities() 4370 s.Require().NoError(err) 4371 s.Require().Len(c, 1) 4372 d, err := json.Marshal(c) 4373 s.Require().NoError(err) 4374 4375 type Image struct { 4376 Uri string `json:"uri"` 4377 } 4378 var communityData []struct { 4379 Images map[string]Image `json:"images"` 4380 CommunityTokensMetadata []struct { 4381 Image string `json:"image"` 4382 Symbol string `json:"symbol"` 4383 } `json:"communityTokensMetadata"` 4384 } 4385 err = json.Unmarshal(d, &communityData) 4386 s.Require().NoError(err) 4387 // Check community description image 4388 s.Require().NotEmpty(communityData[0].Images[identImageName]) 4389 image := communityData[0].Images[identImageName] 4390 s.T().Log(fmt.Sprintf("Image URL (%s):", identImageName), image) 4391 e, err := s.fetchImage(image.Uri) 4392 s.Require().NoError(err) 4393 s.Require().Equal(identImagePayload, e) 4394 4395 imageUrlWithoutCommunityID, err := s.removeUrlParam(image.Uri, "communityID") 4396 s.Require().NoError(err) 4397 e, err = s.fetchImage(imageUrlWithoutCommunityID) 4398 s.Require().NoError(err) 4399 s.Require().Len(e, 0) 4400 4401 imageUrlWithWrongCommunityID, err := s.updateUrlParam(image.Uri, "communityID", "0x0") 4402 s.Require().NoError(err) 4403 e, err = s.fetchImage(imageUrlWithWrongCommunityID) 4404 s.Require().NoError(err) 4405 s.Require().Len(e, 0) 4406 4407 // Check communityTokensMetadata image 4408 s.Require().NotEmpty(communityData[0].CommunityTokensMetadata) 4409 tokenImageUrl := communityData[0].CommunityTokensMetadata[0].Image 4410 s.T().Log("Community Token Metadata Image:", tokenImageUrl) 4411 s.T().Log("Community Token Metadata Symbol:", communityData[0].CommunityTokensMetadata[0].Symbol) 4412 f, err := s.fetchImage(tokenImageUrl) 4413 s.Require().NoError(err) 4414 tokenImagePayload, err := images.GetPayloadFromURI(tokenImageInBase64) 4415 s.Require().NoError(err) 4416 s.Require().Equal(tokenImagePayload, f) 4417 4418 tokenImageUrlWithoutCommunityID, err := s.removeUrlParam(tokenImageUrl, "communityID") 4419 s.Require().NoError(err) 4420 f, err = s.fetchImage(tokenImageUrlWithoutCommunityID) 4421 s.Require().NoError(err) 4422 s.Require().Len(f, 0) 4423 4424 tokenImageUrlWithWrongCommunityID, err := s.updateUrlParam(tokenImageUrl, "communityID", "0x0") 4425 s.Require().NoError(err) 4426 f, err = s.fetchImage(tokenImageUrlWithWrongCommunityID) 4427 s.Require().NoError(err) 4428 s.Require().Len(f, 0) 4429 4430 tokenImageUrlWithoutSymbol, err := s.removeUrlParam(tokenImageUrl, "symbol") 4431 s.Require().NoError(err) 4432 f, err = s.fetchImage(tokenImageUrlWithoutSymbol) 4433 s.Require().NoError(err) 4434 s.Require().Len(f, 0) 4435 4436 tokenImageUrlWithWrongSymbol, err := s.updateUrlParam(tokenImageUrl, "symbol", "WRONG") 4437 s.Require().NoError(err) 4438 f, err = s.fetchImage(tokenImageUrlWithWrongSymbol) 4439 s.Require().NoError(err) 4440 s.Require().Len(f, 0) 4441 } 4442 4443 func (s *MessengerCommunitiesSuite) updateUrlParam(rawURL string, name, val string) (string, error) { 4444 parsedURL, err := url.Parse(rawURL) 4445 if err != nil { 4446 return "", err 4447 } 4448 4449 queryParams := parsedURL.Query() 4450 queryParams.Set(name, val) 4451 parsedURL.RawQuery = queryParams.Encode() 4452 return parsedURL.String(), nil 4453 } 4454 4455 func (s *MessengerCommunitiesSuite) removeUrlParam(rawURL, name string) (string, error) { 4456 parsedURL, err := url.Parse(rawURL) 4457 if err != nil { 4458 return "", err 4459 } 4460 4461 queryParams := parsedURL.Query() 4462 queryParams.Del(name) 4463 parsedURL.RawQuery = queryParams.Encode() 4464 return parsedURL.String(), nil 4465 } 4466 4467 func (s *MessengerCommunitiesSuite) fetchImage(fullURL string) ([]byte, error) { 4468 req, err := http.NewRequest("GET", fullURL, nil) 4469 if err != nil { 4470 return nil, err 4471 } 4472 4473 client := &http.Client{ 4474 Transport: &http.Transport{ 4475 TLSClientConfig: &tls.Config{ 4476 MinVersion: tls.VersionTLS12, 4477 InsecureSkipVerify: true, // nolint: gosec 4478 }, 4479 }, 4480 } 4481 4482 resp, err := client.Do(req) 4483 if err != nil { 4484 return nil, err 4485 } 4486 defer resp.Body.Close() 4487 return ioutil.ReadAll(resp.Body) 4488 } 4489 4490 func (s *MessengerCommunitiesSuite) TestMemberMessagesHasImageLink() { 4491 // GIVEN 4492 addMediaServer := func(messenger *Messenger) { 4493 mediaServer, err := server.NewMediaServer(messenger.database, nil, nil, nil) 4494 s.Require().NoError(err) 4495 s.Require().NoError(mediaServer.Start()) 4496 messenger.SetMediaServer(mediaServer) 4497 } 4498 addMediaServer(s.alice) 4499 addMediaServer(s.bob) 4500 addMediaServer(s.owner) 4501 community, communityChat := s.createCommunity() 4502 4503 advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) 4504 s.joinCommunity(community, s.owner, s.alice) 4505 4506 advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) 4507 s.joinCommunity(community, s.owner, s.bob) 4508 4509 // WHEN: alice sends an image message 4510 sentMessage := s.sendImageToCommunity(s.alice, communityChat.ID) 4511 4512 // THEN: everyone see alice message with image link 4513 requireMessageWithImage := func(messenger *Messenger, memberPubKey string, communityID string) { 4514 storedMessages, err := messenger.GetCommunityMemberAllMessages( 4515 &requests.CommunityMemberMessages{ 4516 CommunityID: communityID, 4517 MemberPublicKey: memberPubKey}) 4518 s.Require().NoError(err) 4519 s.Require().Equal(1, len(storedMessages)) 4520 memberMessage := storedMessages[0] 4521 s.Require().Equal(sentMessage.ID, memberMessage.ID) 4522 s.Require().True(strings.HasPrefix(memberMessage.ImageLocalURL, "https://Localhost")) 4523 } 4524 communityID := community.IDString() 4525 alicePubKey := s.alice.IdentityPublicKeyString() 4526 4527 requireMessageWithImage(s.owner, alicePubKey, communityID) 4528 requireMessageWithImage(s.alice, alicePubKey, communityID) 4529 requireMessageWithImage(s.bob, alicePubKey, communityID) 4530 } 4531 4532 func (s *MessengerCommunitiesSuite) TestOpenAndNotJoinedCommunityNewChannelIsNotEmpty() { 4533 // Create an open community 4534 community, _ := s.createCommunity() 4535 s.Require().Len(community.Chats(), 1) 4536 s.Require().False(community.Encrypted()) 4537 4538 // Bob joins the community 4539 advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) 4540 s.joinCommunity(community, s.owner, s.bob) 4541 4542 // Alice just observes the community 4543 advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) 4544 _, err := s.alice.SpectateCommunity(community.ID()) 4545 s.Require().NoError(err) 4546 4547 aliceCommunity, err := s.alice.GetCommunityByID(community.ID()) 4548 s.Require().NoError(err) 4549 s.Require().Len(aliceCommunity.Chats(), 1) 4550 4551 // Owner creates a new channel 4552 newChannel := &protobuf.CommunityChat{ 4553 Permissions: &protobuf.CommunityPermissions{ 4554 Access: protobuf.CommunityPermissions_AUTO_ACCEPT, 4555 }, 4556 Identity: &protobuf.ChatIdentity{ 4557 DisplayName: "new channel", 4558 Emoji: "", 4559 Description: "chat created after joining the community", 4560 }, 4561 } 4562 4563 response, err := s.owner.CreateCommunityChat(community.ID(), newChannel) 4564 s.Require().NoError(err) 4565 s.Require().Len(response.CommunityChanges, 1) 4566 s.Require().Len(response.CommunityChanges[0].ChatsAdded, 1) 4567 s.Require().Len(response.Communities(), 1) 4568 s.Require().Len(response.Chats(), 1) 4569 s.Require().Len(response.Chats()[0].Members, 2) 4570 for _, chat := range response.Communities()[0].Chats() { 4571 s.Require().Len(chat.Members, 2) 4572 } 4573 4574 // Check Alice gets the correct member list for a new channel 4575 _, err = WaitOnMessengerResponse( 4576 s.alice, 4577 func(r *MessengerResponse) bool { 4578 if len(r.Chats()) == 1 && len(r.Communities()) > 0 { 4579 for _, chat := range r.Chats() { 4580 s.Require().Len(chat.Members, 2) 4581 } 4582 for _, chat := range r.Communities()[0].Chats() { 4583 s.Require().Len(chat.Members, 2) 4584 } 4585 return true 4586 } 4587 return false 4588 }, 4589 "no commiunity message for Alice", 4590 ) 4591 s.Require().NoError(err) 4592 4593 aliceCommunity, err = s.alice.GetCommunityByID(community.ID()) 4594 s.Require().NoError(err) 4595 s.Require().Len(aliceCommunity.Chats(), 2) 4596 for _, chat := range aliceCommunity.Chats() { 4597 s.Require().Len(chat.Members, 2) 4598 } 4599 } 4600 4601 func (s *MessengerCommunitiesSuite) sendMention(sender *Messenger, chatID string) *common.Message { 4602 ctx := context.Background() 4603 messageToSend := common.NewMessage() 4604 messageToSend.ChatId = chatID 4605 messageToSend.ContentType = protobuf.ChatMessage_TEXT_PLAIN 4606 messageToSend.Text = "Hello @" + common.EveryoneMentionTag 4607 4608 response, err := sender.SendChatMessage(ctx, messageToSend) 4609 s.Require().NoError(err) 4610 s.Require().Len(response.Messages(), 1) 4611 s.Require().True(response.Messages()[0].Mentioned) 4612 return response.Messages()[0] 4613 } 4614 4615 func (s *MessengerCommunitiesSuite) TestAliceDoesNotReceiveMentionWhenSpectating() { 4616 // GIVEN: Create an open community 4617 community, communityChat := s.createCommunity() 4618 community, err := s.owner.GetCommunityByID(community.ID()) 4619 s.Require().NoError(err) 4620 s.Require().Len(community.Chats(), 1) 4621 s.Require().False(community.Encrypted()) 4622 4623 // Alice SPECTATES the community 4624 advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) 4625 _, err = s.alice.SpectateCommunity(community.ID()) 4626 s.Require().NoError(err) 4627 4628 aliceCommunity, err := s.alice.GetCommunityByID(community.ID()) 4629 s.Require().NoError(err) 4630 s.Require().Contains(aliceCommunity.ChatIDs(), communityChat.ID) 4631 4632 // Bob JOINS the community 4633 advertiseCommunityTo(&s.Suite, community, s.owner, s.bob) 4634 s.joinCommunity(community, s.owner, s.bob) 4635 4636 // Check Alice gets the updated community 4637 _, err = WaitOnMessengerResponse( 4638 s.alice, 4639 func(r *MessengerResponse) bool { 4640 return len(r.Communities()) > 0 && r.Communities()[0].MembersCount() == 2 4641 }, 4642 "no community updates for Alice", 4643 ) 4644 s.Require().NoError(err) 4645 4646 // WHEN: Bob sends a message to a channel with mention 4647 sentMessage := s.sendMention(s.bob, communityChat.ID) 4648 4649 // THEN: Check Alice gets the message, but no activity center notification 4650 _, err = WaitOnMessengerResponse( 4651 s.alice, 4652 func(r *MessengerResponse) bool { 4653 return len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 0 && 4654 r.Messages()[0].ID == sentMessage.ID 4655 }, 4656 "no message for Alice", 4657 ) 4658 s.Require().NoError(err) 4659 4660 // Alice joins community 4661 s.joinCommunity(community, s.owner, s.alice) 4662 4663 // Bob sends a message with mention 4664 sentMessage = s.sendMention(s.bob, communityChat.ID) 4665 4666 // Check Alice gets the message and activity center notification 4667 _, err = WaitOnMessengerResponse( 4668 s.alice, 4669 func(r *MessengerResponse) bool { 4670 return len(r.Messages()) == 1 && len(r.ActivityCenterNotifications()) == 1 && 4671 r.Messages()[0].ID == sentMessage.ID && r.ActivityCenterNotifications()[0].Message.ID == sentMessage.ID && 4672 r.ActivityCenterNotifications()[0].Type == ActivityCenterNotificationTypeMention 4673 }, 4674 "no message for Alice", 4675 ) 4676 s.Require().NoError(err) 4677 } 4678 4679 // this test simulate the scenario, when we are leaving the community and after the leave 4680 // receiving outdated COMMUNITY_REQUEST_TO_JOIN_RESPONSE and joining the community again 4681 func (s *MessengerCommunitiesSuite) TestAliceDidNotProcessOutdatedCommunityRequestToJoinResponse() { 4682 community, _ := s.createCommunity() 4683 4684 advertiseCommunityTo(&s.Suite, community, s.owner, s.alice) 4685 s.joinCommunity(community, s.owner, s.alice) 4686 4687 response, err := s.alice.LeaveCommunity(community.ID()) 4688 s.Require().NoError(err) 4689 s.Require().Len(response.Communities(), 1) 4690 s.Require().False(response.Communities()[0].Joined()) 4691 4692 // double-check that alice left the community 4693 community, err = s.alice.GetCommunityByID(community.ID()) 4694 s.Require().NoError(err) 4695 s.Require().False(community.Joined()) 4696 4697 // prepare the same request to join response 4698 community, err = s.owner.GetCommunityByID(community.ID()) 4699 s.Require().NoError(err) 4700 4701 grant, err := community.BuildGrant(s.alice.IdentityPublicKey(), "") 4702 s.Require().NoError(err) 4703 4704 var key *ecdsa.PrivateKey 4705 if s.owner.transport.WakuVersion() == 2 { 4706 key, err = s.owner.transport.RetrievePubsubTopicKey(community.PubsubTopic()) 4707 s.Require().NoError(err) 4708 } 4709 4710 encryptedDescription, err := community.EncryptedDescription() 4711 s.Require().NoError(err) 4712 4713 requestToJoinResponse := &protobuf.CommunityRequestToJoinResponse{ 4714 Clock: community.Clock(), 4715 Accepted: true, 4716 CommunityId: community.ID(), 4717 Community: encryptedDescription, 4718 Grant: grant, 4719 ProtectedTopicPrivateKey: crypto.FromECDSA(key), 4720 Shard: community.Shard().Protobuffer(), 4721 } 4722 4723 // alice handle duplicated request to join response 4724 state := &ReceivedMessageState{ 4725 Response: &MessengerResponse{}, 4726 CurrentMessageState: &CurrentMessageState{ 4727 PublicKey: community.ControlNode(), 4728 }, 4729 } 4730 4731 err = s.alice.HandleCommunityRequestToJoinResponse(state, requestToJoinResponse, nil) 4732 s.Require().Error(err, ErrOutdatedCommunityRequestToJoin) 4733 4734 // alice receives new request to join when she's already joined 4735 // Note: requestToJoinResponse clock is stored as milliseconds, but requestToJoin in database stored 4736 // as seconds 4737 requestToJoinResponse.Clock = requestToJoinResponse.Clock + 1000 4738 err = s.alice.HandleCommunityRequestToJoinResponse(state, requestToJoinResponse, nil) 4739 s.Require().NoError(err) 4740 } 4741 4742 func (s *MessengerCommunitiesSuite) TestIgnoreOutdatedCommunityDescription() { 4743 community, _ := s.createCommunity() 4744 wrappedDescription1, err := community.ToProtocolMessageBytes() 4745 s.Require().NoError(err) 4746 signer, description1, err := communities.UnwrapCommunityDescriptionMessage(wrappedDescription1) 4747 s.Require().NoError(err) 4748 4749 _, err = community.AddMember(&s.alice.identity.PublicKey, []protobuf.CommunityMember_Roles{}, community.Clock()) 4750 s.Require().NoError(err) 4751 wrappedDescription2, err := community.ToProtocolMessageBytes() 4752 s.Require().NoError(err) 4753 _, description2, err := communities.UnwrapCommunityDescriptionMessage(wrappedDescription2) 4754 s.Require().NoError(err) 4755 4756 _, err = community.AddMember(&s.bob.identity.PublicKey, []protobuf.CommunityMember_Roles{}, community.Clock()) 4757 s.Require().NoError(err) 4758 wrappedDescription3, err := community.ToProtocolMessageBytes() 4759 s.Require().NoError(err) 4760 _, description3, err := communities.UnwrapCommunityDescriptionMessage(wrappedDescription3) 4761 s.Require().NoError(err) 4762 4763 s.Require().Less(description1.Clock, description2.Clock) 4764 s.Require().Less(description2.Clock, description3.Clock) 4765 4766 // Handle first community description 4767 { 4768 messageState := s.bob.buildMessageState() 4769 err = s.bob.handleCommunityDescription(messageState, signer, description1, wrappedDescription1, nil, community.Shard().Protobuffer()) 4770 s.Require().NoError(err) 4771 s.Require().Len(messageState.Response.Communities(), 1) 4772 s.Require().Equal(description1.Clock, messageState.Response.Communities()[0].Clock()) 4773 } 4774 4775 // Handle third community description 4776 { 4777 messageState := s.bob.buildMessageState() 4778 err = s.bob.handleCommunityDescription(messageState, signer, description3, wrappedDescription3, nil, community.Shard().Protobuffer()) 4779 s.Require().NoError(err) 4780 s.Require().Len(messageState.Response.Communities(), 1) 4781 s.Require().Equal(description3.Clock, messageState.Response.Communities()[0].Clock()) 4782 4783 communityFromDB, err := s.bob.communitiesManager.GetByID(community.ID()) 4784 s.Require().NoError(err) 4785 s.Require().Equal(description3.Clock, communityFromDB.Clock()) 4786 s.Require().Len(communityFromDB.Members(), 3) 4787 } 4788 4789 // Handle second (out of order) community description 4790 // It should be ignored 4791 { 4792 messageState := s.bob.buildMessageState() 4793 err = s.bob.handleCommunityDescription(messageState, signer, description2, wrappedDescription2, nil, community.Shard().Protobuffer()) 4794 s.Require().Len(messageState.Response.Communities(), 0) 4795 s.Require().Len(messageState.Response.CommunityChanges, 0) 4796 s.Require().ErrorIs(err, communities.ErrInvalidCommunityDescriptionClockOutdated) 4797 4798 communityFromDB, err := s.bob.communitiesManager.GetByID(community.ID()) 4799 s.Require().NoError(err) 4800 s.Require().Equal(description3.Clock, communityFromDB.Clock()) 4801 s.Require().Len(communityFromDB.Members(), 3) 4802 } 4803 } 4804 4805 func (s *MessengerCommunitiesSuite) mockPermissionCheckerForAllMessenger() { 4806 s.owner.communitiesManager.PermissionChecker = &testPermissionChecker{} 4807 s.alice.communitiesManager.PermissionChecker = &testPermissionChecker{} 4808 s.bob.communitiesManager.PermissionChecker = &testPermissionChecker{} 4809 }