Signal-Android/app/src/test/java/org/thoughtcrime/securesms/groups/GroupManagerV2Test_edit.kt

159 wiersze
6.4 KiB
Kotlin

@file:Suppress("ClassName")
package org.thoughtcrime.securesms.groups
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers
import org.hamcrest.Matchers.`is`
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.Mockito
import org.mockito.Mockito.mock
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.signal.core.util.ThreadUtil
import org.signal.core.util.logging.Log
import org.signal.storageservice.protos.groups.Member
import org.signal.storageservice.protos.groups.local.DecryptedGroup
import org.signal.storageservice.protos.groups.local.DecryptedMember
import org.signal.zkgroup.groups.GroupMasterKey
import org.signal.zkgroup.groups.GroupSecretParams
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.groups.v2.GroupCandidateHelper
import org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.testutil.SystemOutLogger
import org.thoughtcrime.securesms.util.Hex
import org.whispersystems.libsignal.logging.SignalProtocolLoggerProvider
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations
import org.whispersystems.signalservice.api.push.ACI
import org.whispersystems.signalservice.api.push.ServiceId
import java.util.UUID
@RunWith(RobolectricTestRunner::class)
@Config(manifest = Config.NONE, application = Application::class)
class GroupManagerV2Test_edit {
companion object {
val server: TestZkGroupServer = TestZkGroupServer()
val masterKey: GroupMasterKey = GroupMasterKey(Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"))
val groupSecretParams: GroupSecretParams = GroupSecretParams.deriveFromMasterKey(masterKey)
val groupId: GroupId.V2 = GroupId.v2(masterKey)
val selfAci: ACI = ACI.from(UUID.randomUUID())
val otherSid: ServiceId = ServiceId.from(UUID.randomUUID())
val selfAndOthers: List<DecryptedMember> = listOf(member(selfAci), member(otherSid))
val others: List<DecryptedMember> = listOf(member(otherSid))
}
private lateinit var groupDatabase: GroupDatabase
private lateinit var groupsV2API: GroupsV2Api
private lateinit var groupsV2Operations: GroupsV2Operations
private lateinit var groupsV2Authorization: GroupsV2Authorization
private lateinit var groupsV2StateProcessor: GroupsV2StateProcessor
private lateinit var groupCandidateHelper: GroupCandidateHelper
private lateinit var sendGroupUpdateHelper: GroupManagerV2.SendGroupUpdateHelper
private lateinit var groupOperations: GroupsV2Operations.GroupOperations
private lateinit var patchedDecryptedGroup: ArgumentCaptor<DecryptedGroup>
private lateinit var manager: GroupManagerV2
@get:Rule
val signalStore: SignalStoreRule = SignalStoreRule()
@Suppress("UsePropertyAccessSyntax")
@Before
fun setUp() {
ThreadUtil.enforceAssertions = false
Log.initialize(SystemOutLogger())
SignalProtocolLoggerProvider.setProvider(CustomSignalProtocolLogger())
val clientZkOperations = ClientZkOperations(server.getServerPublicParams())
groupDatabase = mock(GroupDatabase::class.java)
groupsV2API = mock(GroupsV2Api::class.java)
groupsV2Operations = GroupsV2Operations(clientZkOperations)
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
groupsV2StateProcessor = mock(GroupsV2StateProcessor::class.java)
groupCandidateHelper = mock(GroupCandidateHelper::class.java)
sendGroupUpdateHelper = mock(GroupManagerV2.SendGroupUpdateHelper::class.java)
groupOperations = groupsV2Operations.forGroup(groupSecretParams)
patchedDecryptedGroup = ArgumentCaptor.forClass(DecryptedGroup::class.java)
manager = GroupManagerV2(
ApplicationProvider.getApplicationContext(),
groupDatabase,
groupsV2API,
groupsV2Operations,
groupsV2Authorization,
groupsV2StateProcessor,
selfAci,
groupCandidateHelper,
sendGroupUpdateHelper
)
}
private fun given(init: GroupStateTestData.() -> Unit) {
val data = GroupStateTestData(masterKey, groupOperations)
data.init()
Mockito.doReturn(data.groupRecord).`when`(groupDatabase).getGroup(groupId)
Mockito.doReturn(data.groupRecord.get()).`when`(groupDatabase).requireGroup(groupId)
Mockito.doReturn(GroupManagerV2.RecipientAndThread(Recipient.UNKNOWN, 1)).`when`(sendGroupUpdateHelper).sendGroupUpdate(Mockito.eq(masterKey), Mockito.any(), Mockito.any())
Mockito.doReturn(data.groupChange!!).`when`(groupsV2API).patchGroup(Mockito.any(), Mockito.any(), Mockito.any())
}
private fun editGroup(perform: GroupManagerV2.GroupEditor.() -> Unit) {
manager.edit(groupId).use { it.perform() }
}
private fun then(then: (DecryptedGroup) -> Unit) {
Mockito.verify(groupDatabase).update(Mockito.eq(groupId), patchedDecryptedGroup.capture())
then(patchedDecryptedGroup.value)
}
@Test
fun `when you are the only admin, and then leave the group, server upgrades all other members to administrators and lets you leave`() {
given {
localState(
revision = 5,
members = listOf(
member(selfAci, role = Member.Role.ADMINISTRATOR),
member(otherSid)
)
)
groupChange(6) {
source(selfAci)
deleteMember(selfAci)
modifyRole(otherSid, Member.Role.ADMINISTRATOR)
}
}
editGroup {
leaveGroup()
}
then { patchedGroup ->
assertThat("Revision updated by one", patchedGroup.revision, `is`(6))
assertThat("Self is no longer in the group", patchedGroup.membersList.find { it.uuid == selfAci.toByteString() }, Matchers.nullValue())
assertThat("Other is now an admin in the group", patchedGroup.membersList.find { it.uuid == otherSid.toByteString() }?.role, `is`(Member.Role.ADMINISTRATOR))
}
}
}