diff --git a/protocol/communities/manager.go b/protocol/communities/manager.go index de68d5143..48910583b 100644 --- a/protocol/communities/manager.go +++ b/protocol/communities/manager.go @@ -2422,6 +2422,14 @@ func (m *Manager) HandleCommunityRequestToJoin(signer *ecdsa.PublicKey, receiver if err != nil { return nil, nil, err } + case RequestToJoinStateAccepted: + // if member leaved the community and tries to request to join again + if !community.HasMember(signer) { + err = m.SaveRequestToJoin(requestToJoin) + if err != nil { + return nil, nil, err + } + } } } diff --git a/protocol/communities_events_token_master_test.go b/protocol/communities_events_token_master_test.go index 354eb9033..14f48b49d 100644 --- a/protocol/communities_events_token_master_test.go +++ b/protocol/communities_events_token_master_test.go @@ -274,3 +274,11 @@ func (s *TokenMasterCommunityEventsSuite) TestReceiveRequestsToJoinWithRevealedA bob := s.newMessenger(accountPassword, []string{bobAccountAddress}) testMemberReceiveRequestsToJoinAfterGettingNewRole(s, bob, protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER) } + +func (s *TokenMasterCommunityEventsSuite) TestTokenMasterAcceptsRequestToJoinAfterMemberLeave() { + community := setUpOnRequestCommunityAndRoles(s, protobuf.CommunityMember_ROLE_TOKEN_MASTER, []*Messenger{}) + + // set up additional user that will send request to join + user := s.newMessenger("", []string{}) + testPrivilegedMemberAcceptsRequestToJoinAfterMemberLeave(s, community, user) +} diff --git a/protocol/communities_events_utils_test.go b/protocol/communities_events_utils_test.go index c111d77f2..425d1f766 100644 --- a/protocol/communities_events_utils_test.go +++ b/protocol/communities_events_utils_test.go @@ -2224,3 +2224,236 @@ func waitAndCheckRequestsToJoin(s *suite.Suite, user *Messenger, expectedLength ) s.Require().NoError(err) } + +func testPrivilegedMemberAcceptsRequestToJoinAfterMemberLeave(base CommunityEventsTestsInterface, community *communities.Community, user *Messenger) { + // set up additional user that will send request to join + _, err := user.Start() + + s := base.GetSuite() + + s.Require().NoError(err) + defer TearDownMessenger(s, user) + + advertiseCommunityToUserOldWay(s, community, base.GetControlNode(), user) + + // user sends request to join + requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID(), ENSName: "testName"} + response, err := user.RequestToJoinCommunity(requestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + sentRequest := response.RequestsToJoinCommunity[0] + + checkRequestToJoin := func(r *MessengerResponse) bool { + if len(r.RequestsToJoinCommunity) == 0 { + return false + } + for _, request := range r.RequestsToJoinCommunity { + if request.ENSName == requestToJoin.ENSName { + return true + } + } + return false + } + // event sender receives request to join + response, err = WaitOnMessengerResponse( + base.GetEventSender(), + checkRequestToJoin, + "event sender did not receive community request to join", + ) + s.Require().NoError(err) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + // control node receives request to join + _, err = WaitOnMessengerResponse( + base.GetControlNode(), + checkRequestToJoin, + "event sender did not receive community request to join", + ) + s.Require().NoError(err) + + acceptRequestToJoin := &requests.AcceptRequestToJoinCommunity{ID: sentRequest.ID} + response, err = base.GetEventSender().AcceptRequestToJoinCommunity(acceptRequestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.Communities(), 1) + // we don't expect `user` to be a member already, because `eventSender` merely + // forwards its accept decision to the control node + s.Require().False(response.Communities()[0].HasMember(&user.identity.PublicKey)) + + // at this point, the request to join is marked as accepted by GetEventSender node + acceptedRequestsPending, err := base.GetEventSender().AcceptedPendingRequestsToJoinForCommunity(community.ID()) + s.Require().NoError(err) + s.Require().Len(acceptedRequestsPending, 1) + s.Require().Equal(acceptedRequestsPending[0].PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) + + // control node receives community event with accepted membership request + _, err = WaitOnMessengerResponse( + base.GetControlNode(), + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) + }, + "control node did not receive community request to join response", + ) + s.Require().NoError(err) + + // at this point, the request to join is marked as accepted by control node + acceptedRequests, err := base.GetControlNode().AcceptedRequestsToJoinForCommunity(community.ID()) + s.Require().NoError(err) + // we expect 3 here (1 event senders, 1 member + 1 from user) + s.Require().Len(acceptedRequests, 3) + s.Require().Equal(acceptedRequests[2].PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) + + // user receives updated community + _, err = WaitOnMessengerResponse( + user, + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) + }, + "alice did not receive community request to join response", + ) + s.Require().NoError(err) + + // event sender receives updated community + _, err = WaitOnMessengerResponse( + base.GetEventSender(), + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) + }, + "event sender did not receive community with the new member", + ) + s.Require().NoError(err) + + // check control node notify event sender about accepting request to join + _, err = WaitOnMessengerResponse( + base.GetEventSender(), + func(r *MessengerResponse) bool { + acceptedRequests, err := base.GetEventSender().AcceptedRequestsToJoinForCommunity(community.ID()) + return err == nil && len(acceptedRequests) == 2 && (acceptedRequests[1].PublicKey == common.PubkeyToHex(&user.identity.PublicKey)) + }, + "no updates from control node", + ) + + s.Require().NoError(err) + + acceptedRequestsPending, err = base.GetEventSender().AcceptedPendingRequestsToJoinForCommunity(community.ID()) + s.Require().NoError(err) + s.Require().Len(acceptedRequestsPending, 0) + + // user leaves the community + response, err = user.LeaveCommunity(community.ID()) + s.Require().NoError(err) + s.Require().Len(response.Communities(), 1) + s.Require().False(response.Communities()[0].Joined()) + + checkMemberLeave := func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && !r.Communities()[0].HasMember(&user.identity.PublicKey) + } + + // check control node received member leave msg + _, err = WaitOnMessengerResponse( + base.GetControlNode(), + checkMemberLeave, + "control node did not receive member leave msg", + ) + s.Require().NoError(err) + + // check event sender received member leave update from ControlNode + _, err = WaitOnMessengerResponse( + base.GetControlNode(), + checkMemberLeave, + "event sender did not receive member leave update", + ) + s.Require().NoError(err) + + // user tries to rejoin again + response, err = user.RequestToJoinCommunity(requestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + // event sender receives request to join + response, err = WaitOnMessengerResponse( + base.GetEventSender(), + checkRequestToJoin, + "event sender did not receive community request to join", + ) + s.Require().NoError(err) + s.Require().Len(response.RequestsToJoinCommunity, 1) + + // control node receives request to join + _, err = WaitOnMessengerResponse( + base.GetControlNode(), + checkRequestToJoin, + "event sender did not receive community request to join", + ) + s.Require().NoError(err) + + response, err = base.GetEventSender().AcceptRequestToJoinCommunity(acceptRequestToJoin) + s.Require().NoError(err) + s.Require().NotNil(response) + s.Require().Len(response.Communities(), 1) + // we don't expect `user` to be a member already, because `eventSender` merely + // forwards its accept decision to the control node + s.Require().False(response.Communities()[0].HasMember(&user.identity.PublicKey)) + + // at this point, the request to join is marked as accepted pending by GetEventSender node + acceptedRequestsPending, err = base.GetEventSender().AcceptedPendingRequestsToJoinForCommunity(community.ID()) + s.Require().NoError(err) + s.Require().Len(acceptedRequestsPending, 1) + s.Require().Equal(acceptedRequestsPending[0].PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) + + // control node receives community event with accepted membership request + _, err = WaitOnMessengerResponse( + base.GetControlNode(), + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) + }, + "control node did not receive community request to join response", + ) + s.Require().NoError(err) + + // at this point, the request to join is marked as accepted by control node + acceptedRequests, err = base.GetControlNode().AcceptedRequestsToJoinForCommunity(community.ID()) + s.Require().NoError(err) + // we expect 3 here (1 event senders, 1 member + 1 from user) + s.Require().Len(acceptedRequests, 3) + s.Require().Equal(acceptedRequests[2].PublicKey, common.PubkeyToHex(&user.identity.PublicKey)) + + // user receives updated community + _, err = WaitOnMessengerResponse( + user, + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) + }, + "user did not receive community request to join response", + ) + s.Require().NoError(err) + + // event sender receives updated community + _, err = WaitOnMessengerResponse( + base.GetEventSender(), + func(r *MessengerResponse) bool { + return len(r.Communities()) > 0 && r.Communities()[0].HasMember(&user.identity.PublicKey) + }, + "event sender did not receive community with the new member", + ) + s.Require().NoError(err) + + // check control node notify event sender about accepting request to join + _, err = WaitOnMessengerResponse( + base.GetEventSender(), + func(r *MessengerResponse) bool { + acceptedRequests, err := base.GetEventSender().AcceptedRequestsToJoinForCommunity(community.ID()) + return err == nil && len(acceptedRequests) == 2 && (acceptedRequests[1].PublicKey == common.PubkeyToHex(&user.identity.PublicKey)) + }, + "no updates from control node", + ) + + s.Require().NoError(err) + + acceptedRequestsPending, err = base.GetEventSender().AcceptedPendingRequestsToJoinForCommunity(community.ID()) + s.Require().NoError(err) + s.Require().Len(acceptedRequestsPending, 0) +} diff --git a/protocol/communities_messenger_admin_test.go b/protocol/communities_messenger_admin_test.go index 2eb5803ff..0efe032da 100644 --- a/protocol/communities_messenger_admin_test.go +++ b/protocol/communities_messenger_admin_test.go @@ -433,3 +433,11 @@ func (s *AdminCommunityEventsSuite) TestAdminDoesNotHaveRejectedEventsLoop() { }, "no communities in response") s.Require().Error(err) } + +func (s *AdminCommunityEventsSuite) TestAdminAcceptsRequestToJoinAfterMemberLeave() { + community := setUpOnRequestCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN, []*Messenger{}) + + // set up additional user that will send request to join + user := s.newMessenger("", []string{}) + testPrivilegedMemberAcceptsRequestToJoinAfterMemberLeave(s, community, user) +} diff --git a/protocol/messenger_communities.go b/protocol/messenger_communities.go index 400c0c9b9..cff70d069 100644 --- a/protocol/messenger_communities.go +++ b/protocol/messenger_communities.go @@ -351,10 +351,13 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti accept := &requests.AcceptRequestToJoinCommunity{ ID: requestID, } - _, err := m.AcceptRequestToJoinCommunity(accept) + response, err := m.AcceptRequestToJoinCommunity(accept) if err != nil { m.logger.Warn("failed to accept request to join ", zap.Error(err)) } + if m.config.messengerSignalsHandler != nil { + m.config.messengerSignalsHandler.MessengerResponse(response) + } } } @@ -363,10 +366,13 @@ func (m *Messenger) handleCommunitiesSubscription(c chan *communities.Subscripti reject := &requests.DeclineRequestToJoinCommunity{ ID: requestID, } - _, err := m.DeclineRequestToJoinCommunity(reject) + response, err := m.DeclineRequestToJoinCommunity(reject) if err != nil { m.logger.Warn("failed to decline request to join ", zap.Error(err)) } + if m.config.messengerSignalsHandler != nil { + m.config.messengerSignalsHandler.MessengerResponse(response) + } } }