Add group terminate support.

This commit is contained in:
Cody Henthorne
2026-03-19 16:10:26 -04:00
parent 0896718e5c
commit a0c0acb8fc
130 changed files with 1312 additions and 146 deletions

View File

@@ -51,7 +51,7 @@ public final class DecryptedGroupUtil_apply_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
assertEquals("DecryptedGroupUtil and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
27, maxFieldFound);
28, maxFieldFound);
}
@Test
@@ -1084,4 +1084,23 @@ public final class DecryptedGroupUtil_apply_Test {
assertEquals(expectedResult, DecryptedGroupUtil.apply(group, groupChange));
}
@Test
public void apply_terminate_group() throws NotAbleToApplyGroupV2ChangeException {
DecryptedGroup group = new DecryptedGroup.Builder()
.revision(10)
.build();
DecryptedGroupChange groupChange = new DecryptedGroupChange.Builder()
.revision(11)
.terminateGroup(true)
.build();
DecryptedGroup expectedResult = new DecryptedGroup.Builder()
.revision(11)
.terminated(true)
.build();
assertEquals(expectedResult, DecryptedGroupUtil.apply(group, groupChange));
}
}

View File

@@ -41,7 +41,7 @@ public final class DecryptedGroupUtil_empty_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
assertEquals("GroupChangeField and getChangedFields() need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
27, maxFieldFound);
28, maxFieldFound);
}
@Test
@@ -295,6 +295,16 @@ public final class DecryptedGroupUtil_empty_Test {
assertFalse(DecryptedGroupExtensions.isSilent(change));
}
@Test
public void not_empty_with_terminate_group_field_28() {
DecryptedGroupChange change = new DecryptedGroupChange.Builder()
.terminateGroup(true)
.build();
assertFalse(DecryptedGroupExtensions.getChangedFields(change).isEmpty());
assertFalse(DecryptedGroupExtensions.isSilent(change));
}
@Test
public void silent_with_profile_keys_and_banned_members() {
DecryptedGroupChange change = new DecryptedGroupChange.Builder()

View File

@@ -45,7 +45,7 @@ public final class GroupChangeReconstructTest {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroup.class, ProtobufTestUtils.IGNORED_DECRYPTED_GROUP_TAGS);
assertEquals("GroupChangeReconstruct and its tests need updating to account for new fields on " + DecryptedGroup.class.getName(),
13, maxFieldFound);
14, maxFieldFound);
}
@Test
@@ -487,4 +487,22 @@ public final class GroupChangeReconstructTest {
.build(),
decryptedGroupChange);
}
@Test
public void terminate_group() {
DecryptedGroup from = new DecryptedGroup.Builder()
.build();
DecryptedGroup to = new DecryptedGroup.Builder()
.terminated(true)
.build();
DecryptedGroupChange decryptedGroupChange = GroupChangeReconstruct.reconstructGroupChange(from, to);
assertEquals(
new DecryptedGroupChange.Builder()
.terminateGroup(true)
.build(),
decryptedGroupChange);
}
}

View File

@@ -22,7 +22,7 @@ public final class GroupChangeUtil_changeIsEmpty_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(GroupChange.Actions.class);
assertEquals("GroupChangeUtil and its tests need updating to account for new fields on " + GroupChange.Actions.class.getName(),
27, maxFieldFound);
28, maxFieldFound);
}
@Test
@@ -245,4 +245,13 @@ public final class GroupChangeUtil_changeIsEmpty_Test {
assertFalse(GroupChangeUtil.changeIsEmpty(actions));
}
@Test
public void not_empty_with_terminate_group_field_28() {
GroupChange.Actions actions = new GroupChange.Actions.Builder()
.terminate_group(new GroupChange.Actions.TerminateGroupAction())
.build();
assertFalse(GroupChangeUtil.changeIsEmpty(actions));
}
}

View File

@@ -53,7 +53,7 @@ public final class GroupChangeUtil_resolveConflict_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
27, maxFieldFound);
28, maxFieldFound);
}
/**
@@ -66,7 +66,7 @@ public final class GroupChangeUtil_resolveConflict_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(GroupChange.Actions.class);
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + GroupChange.class.getName(),
27, maxFieldFound);
28, maxFieldFound);
}
/**
@@ -79,7 +79,7 @@ public final class GroupChangeUtil_resolveConflict_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroup.class, ProtobufTestUtils.IGNORED_DECRYPTED_GROUP_TAGS);
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroup.class.getName(),
13, maxFieldFound);
14, maxFieldFound);
}
@@ -993,4 +993,53 @@ public final class GroupChangeUtil_resolveConflict_Test {
GroupChange.Actions resolvedActions = GroupChangeUtil.resolveConflict(groupState, decryptedChange, change).build();
assertTrue(GroupChangeUtil.changeIsEmpty(resolvedActions));
}
@Test
public void field_28__terminate_group_preserved_when_group_not_terminated() {
DecryptedGroup groupState = new DecryptedGroup.Builder()
.revision(5)
.terminated(false)
.build();
DecryptedGroupChange conflictingChange = new DecryptedGroupChange.Builder()
.revision(6)
.terminateGroup(true)
.build();
GroupChange.Actions conflictingActions = new GroupChange.Actions.Builder()
.version(6)
.terminate_group(new GroupChange.Actions.TerminateGroupAction())
.build();
GroupChange.Actions expectedResolvedActions = new GroupChange.Actions.Builder()
.version(6)
.terminate_group(new GroupChange.Actions.TerminateGroupAction())
.build();
assertEquals(expectedResolvedActions, GroupChangeUtil.resolveConflict(groupState, conflictingChange, conflictingActions).build());
}
@Test
public void field_28__terminate_group_removed_when_group_already_terminated() {
DecryptedGroup groupState = new DecryptedGroup.Builder()
.revision(5)
.terminated(true)
.build();
DecryptedGroupChange conflictingChange = new DecryptedGroupChange.Builder()
.revision(6)
.terminateGroup(true)
.build();
GroupChange.Actions conflictingActions = new GroupChange.Actions.Builder()
.version(6)
.terminate_group(new GroupChange.Actions.TerminateGroupAction())
.build();
GroupChange.Actions expectedResolvedActions = new GroupChange.Actions.Builder()
.version(6)
.build();
assertEquals(expectedResolvedActions, GroupChangeUtil.resolveConflict(groupState, conflictingChange, conflictingActions).build());
}
}

View File

@@ -46,7 +46,7 @@ public final class GroupChangeUtil_resolveConflict_decryptedOnly_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
27, maxFieldFound);
28, maxFieldFound);
}
/**
@@ -59,7 +59,7 @@ public final class GroupChangeUtil_resolveConflict_decryptedOnly_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroup.class, ProtobufTestUtils.IGNORED_DECRYPTED_GROUP_TAGS);
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroup.class.getName(),
13, maxFieldFound);
14, maxFieldFound);
}
@@ -785,4 +785,43 @@ public final class GroupChangeUtil_resolveConflict_decryptedOnly_Test {
DecryptedGroupChange resolvedChanges = GroupChangeUtil.resolveConflict(groupState, decryptedChange).build();
assertTrue(DecryptedGroupExtensions.getChangedFields(resolvedChanges).isEmpty());
}
@Test
public void field_28__terminate_group_preserved_when_group_not_terminated() {
DecryptedGroup groupState = new DecryptedGroup.Builder()
.revision(5)
.terminated(false)
.build();
DecryptedGroupChange conflictingChange = new DecryptedGroupChange.Builder()
.revision(6)
.terminateGroup(true)
.build();
DecryptedGroupChange expectedResolvedChange = new DecryptedGroupChange.Builder()
.revision(6)
.terminateGroup(true)
.build();
assertEquals(expectedResolvedChange, GroupChangeUtil.resolveConflict(groupState, conflictingChange).build());
}
@Test
public void field_28__terminate_group_removed_when_group_already_terminated() {
DecryptedGroup groupState = new DecryptedGroup.Builder()
.revision(5)
.terminated(true)
.build();
DecryptedGroupChange conflictingChange = new DecryptedGroupChange.Builder()
.revision(6)
.terminateGroup(true)
.build();
DecryptedGroupChange expectedResolvedChange = new DecryptedGroupChange.Builder()
.revision(6)
.build();
assertEquals(expectedResolvedChange, GroupChangeUtil.resolveConflict(groupState, conflictingChange).build());
}
}

View File

@@ -73,7 +73,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
assertEquals("GroupV2Operations#decryptChange and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
27,
28,
maxFieldFound);
}
@@ -544,6 +544,16 @@ public final class GroupsV2Operations_decrypt_change_Test {
assertDecryption(encryptedChange, expectedDecryptedChange);
}
@Test
public void can_pass_through_terminate_group_field_28() {
GroupChange.Actions.Builder encryptedChange = groupOperations.createTerminateGroup();
DecryptedGroupChange.Builder expectedDecryptedChange = new DecryptedGroupChange.Builder()
.terminateGroup(true);
assertDecryption(encryptedChange, expectedDecryptedChange);
}
private static ProfileKey newProfileKey() {
try {
return new ProfileKey(Util.getSecretBytes(32));

View File

@@ -58,7 +58,7 @@ public final class GroupsV2Operations_decrypt_group_Test {
int maxFieldFound = getMaxDeclaredFieldNumber(Group.class);
assertEquals("GroupOperations and its tests need updating to account for new fields on " + Group.class.getName(),
13, maxFieldFound);
14, maxFieldFound);
}
@Test
@@ -310,6 +310,17 @@ public final class GroupsV2Operations_decrypt_group_Test {
assertEquals(new DecryptedBannedMember.Builder().serviceIdBytes(member1.toByteString()).build(), decryptedGroup.bannedMembers.get(0));
}
@Test
public void pass_through_terminated_field_14() throws VerificationFailedException, InvalidGroupStateException {
Group group = new Group.Builder()
.terminated(true)
.build();
DecryptedGroup decryptedGroup = groupOperations.decryptGroup(group);
assertEquals(true, decryptedGroup.terminated);
}
private ByteString encryptProfileKey(ACI aci, ProfileKey profileKey) {
return ByteString.of(new ClientZkGroupCipher(groupSecretParams).encryptProfileKey(profileKey, aci.getLibSignalAci()).serialize());
}