Collapse multiple join request/cancels when from a single person.

This commit is contained in:
Cody Henthorne
2022-03-14 20:49:40 -04:00
parent 216059b659
commit 9d1f46da9f
25 changed files with 736 additions and 41 deletions

View File

@@ -21,7 +21,6 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations
import org.whispersystems.signalservice.api.push.DistributionId
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.Optional
import java.util.UUID
fun DecryptedGroupChange.Builder.setNewDescription(description: String) {
newDescription = DecryptedString.newBuilder().setValue(description).build()
@@ -224,21 +223,3 @@ fun decryptedGroup(
return builder.build()
}
fun member(serviceId: UUID, role: Member.Role = Member.Role.DEFAULT, joinedAt: Int = 0): DecryptedMember {
return member(ServiceId.from(serviceId), role, joinedAt)
}
fun member(serviceId: ServiceId, role: Member.Role = Member.Role.DEFAULT, joinedAt: Int = 0): DecryptedMember {
return DecryptedMember.newBuilder()
.setRole(role)
.setUuid(serviceId.toByteString())
.setJoinedAtRevision(joinedAt)
.build()
}
fun requestingMember(serviceId: ServiceId): DecryptedRequestingMember {
return DecryptedRequestingMember.newBuilder()
.setUuid(serviceId.toByteString())
.build()
}

View File

@@ -0,0 +1,83 @@
package org.thoughtcrime.securesms.database.model
import com.google.protobuf.ByteString
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.`is`
import org.junit.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context
import org.thoughtcrime.securesms.groups.v2.ChangeBuilder
import org.thoughtcrime.securesms.util.Base64
import org.whispersystems.signalservice.api.util.UuidUtil
import org.whispersystems.signalservice.internal.push.SignalServiceProtos
import java.util.Random
import java.util.UUID
@Suppress("ClassName")
class MessageRecordTest_createNewContextWithAppendedDeleteJoinRequest {
/**
* Given a non-gv2 message, when I append, then I expect an assertion error
*/
@Test(expected = AssertionError::class)
fun throwOnNonGv2() {
val messageRecord = mock<MessageRecord> {
on { decryptedGroupV2Context } doReturn null
}
MessageRecord.createNewContextWithAppendedDeleteJoinRequest(messageRecord, 0, ByteString.EMPTY)
}
/**
* Given a gv2 empty change, when I append, then I expect an assertion error.
*/
@Test(expected = AssertionError::class)
fun throwOnEmptyGv2Change() {
val groupContext = DecryptedGroupV2Context.getDefaultInstance()
val messageRecord = mock<MessageRecord> {
on { decryptedGroupV2Context } doReturn groupContext
}
MessageRecord.createNewContextWithAppendedDeleteJoinRequest(messageRecord, 0, ByteString.EMPTY)
}
/**
* Given a gv2 requesting member change, when I append, then I expect new group context including the change with a new delete.
*/
@Test
fun appendDeleteToExistingContext() {
val alice = UUID.randomUUID()
val aliceByteString = UuidUtil.toByteString(alice)
val change = ChangeBuilder.changeBy(alice)
.requestJoin(alice)
.build()
.toBuilder()
.setRevision(9)
.build()
val context = DecryptedGroupV2Context.newBuilder()
.setContext(SignalServiceProtos.GroupContextV2.newBuilder().setMasterKey(ByteString.copyFrom(randomBytes())))
.setChange(change)
.build()
val messageRecord = mock<MessageRecord> {
on { decryptedGroupV2Context } doReturn context
}
val newEncodedBody = MessageRecord.createNewContextWithAppendedDeleteJoinRequest(messageRecord, 10, aliceByteString)
val newContext = DecryptedGroupV2Context.parseFrom(Base64.decode(newEncodedBody))
assertThat("revision updated to 10", newContext.change.revision, `is`(10))
assertThat("change should retain join request", newContext.change.newRequestingMembersList[0].uuid, `is`(aliceByteString))
assertThat("change should add delete request", newContext.change.deleteRequestingMembersList[0], `is`(aliceByteString))
}
private fun randomBytes(): ByteArray {
val bytes = ByteArray(32)
Random().nextBytes(bytes)
return bytes
}
}

View File

@@ -27,7 +27,7 @@ import org.thoughtcrime.securesms.SignalStoreRule
import org.thoughtcrime.securesms.TestZkGroupServer
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.GroupStateTestData
import org.thoughtcrime.securesms.database.member
import org.thoughtcrime.securesms.database.model.databaseprotos.member
import org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger

View File

@@ -28,8 +28,8 @@ import org.thoughtcrime.securesms.SignalStoreRule
import org.thoughtcrime.securesms.database.GroupDatabase
import org.thoughtcrime.securesms.database.GroupStateTestData
import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.member
import org.thoughtcrime.securesms.database.requestingMember
import org.thoughtcrime.securesms.database.model.databaseprotos.member
import org.thoughtcrime.securesms.database.model.databaseprotos.requestingMember
import org.thoughtcrime.securesms.database.setNewDescription
import org.thoughtcrime.securesms.database.setNewTitle
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies

View File

@@ -91,6 +91,54 @@ public class GroupV2UpdateMessageUtilTest {
assertFalse(isJustAGroupLeave);
}
@Test
public void isJoinRequestCancel_whenChangeRemovesRequestingMembers_shouldReturnTrue() {
// GIVEN
UUID alice = UUID.randomUUID();
DecryptedGroupChange change = ChangeBuilder.changeBy(alice)
.denyRequest(alice)
.build();
DecryptedGroupV2Context context = DecryptedGroupV2Context.newBuilder()
.setContext(SignalServiceProtos.GroupContextV2.newBuilder()
.setMasterKey(ByteString.copyFrom(randomBytes())))
.setChange(change)
.build();
MessageGroupContext messageGroupContext = new MessageGroupContext(context);
// WHEN
boolean isJoinRequestCancel = GroupV2UpdateMessageUtil.isJoinRequestCancel(messageGroupContext);
// THEN
assertTrue(isJoinRequestCancel);
}
@Test
public void isJoinRequestCancel_whenChangeContainsNoRemoveRequestingMembers_shouldReturnFalse() {
// GIVEN
UUID alice = UUID.randomUUID();
UUID bob = UUID.randomUUID();
DecryptedGroupChange change = ChangeBuilder.changeBy(alice)
.deleteMember(alice)
.addMember(bob)
.build();
DecryptedGroupV2Context context = DecryptedGroupV2Context.newBuilder()
.setContext(SignalServiceProtos.GroupContextV2.newBuilder()
.setMasterKey(ByteString.copyFrom(randomBytes())))
.setChange(change)
.build();
MessageGroupContext messageGroupContext = new MessageGroupContext(context);
// WHEN
boolean isJoinRequestCancel = GroupV2UpdateMessageUtil.isJoinRequestCancel(messageGroupContext);
// THEN
assertFalse(isJoinRequestCancel);
}
private @NonNull byte[] randomBytes() {
byte[] bytes = new byte[32];
new Random().nextBytes(bytes);