Convert GroupTable to kotlin.

Also required converting some tests to mockk.
This commit is contained in:
Greyson Parrelli
2023-01-01 23:05:00 -05:00
parent fecfd7cd78
commit 92b9fda6c7
55 changed files with 1756 additions and 1881 deletions

View File

@@ -13,6 +13,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember
import org.signal.storageservice.protos.groups.local.DecryptedString
import org.signal.storageservice.protos.groups.local.DecryptedTimer
import org.signal.storageservice.protos.groups.local.EnabledState
import org.thoughtcrime.securesms.database.model.GroupRecord
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.recipients.RecipientId
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupHistoryEntry
@@ -99,7 +100,7 @@ class GroupChangeData(private val revision: Int, private val groupOperations: Gr
class GroupStateTestData(private val masterKey: GroupMasterKey, private val groupOperations: GroupsV2Operations.GroupOperations? = null) {
var localState: DecryptedGroup? = null
var groupRecord: Optional<GroupTable.GroupRecord> = Optional.empty()
var groupRecord: Optional<GroupRecord> = Optional.empty()
var serverState: DecryptedGroup? = null
var changeSet: ChangeSet? = null
var groupChange: GroupChange? = null
@@ -172,9 +173,9 @@ fun groupRecord(
avatarDigest: ByteArray = ByteArray(0),
mms: Boolean = false,
distributionId: DistributionId? = null
): Optional<GroupTable.GroupRecord> {
): Optional<GroupRecord> {
return Optional.of(
GroupTable.GroupRecord(
GroupRecord(
id,
recipientId,
decryptedGroup.title,

View File

@@ -4,6 +4,10 @@ package org.thoughtcrime.securesms.groups
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import io.mockk.every
import io.mockk.mockk
import io.mockk.slot
import io.mockk.verify
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers
import org.hamcrest.Matchers.`is`
@@ -11,9 +15,6 @@ 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.Hex
@@ -71,8 +72,6 @@ class GroupManagerV2Test_edit {
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
@@ -87,17 +86,15 @@ class GroupManagerV2Test_edit {
val clientZkOperations = ClientZkOperations(server.getServerPublicParams())
groupTable = mock(GroupTable::class.java)
groupsV2API = mock(GroupsV2Api::class.java)
groupTable = mockk()
groupsV2API = mockk()
groupsV2Operations = GroupsV2Operations(clientZkOperations, 1000)
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
groupsV2StateProcessor = mock(GroupsV2StateProcessor::class.java)
groupCandidateHelper = mock(GroupCandidateHelper::class.java)
sendGroupUpdateHelper = mock(GroupManagerV2.SendGroupUpdateHelper::class.java)
groupsV2Authorization = mockk(relaxed = true)
groupsV2StateProcessor = mockk()
groupCandidateHelper = mockk()
sendGroupUpdateHelper = mockk()
groupOperations = groupsV2Operations.forGroup(groupSecretParams)
patchedDecryptedGroup = ArgumentCaptor.forClass(DecryptedGroup::class.java)
manager = GroupManagerV2(
ApplicationProvider.getApplicationContext(),
groupTable,
@@ -115,12 +112,11 @@ class GroupManagerV2Test_edit {
val data = GroupStateTestData(masterKey, groupOperations)
data.init()
Mockito.doReturn(data.groupRecord).`when`(groupTable).getGroup(groupId)
Mockito.doReturn(data.groupRecord.get()).`when`(groupTable).requireGroup(groupId)
Mockito.doReturn(GroupManagerV2.RecipientAndThread(Recipient.UNKNOWN, 1)).`when`(sendGroupUpdateHelper).sendGroupUpdate(Mockito.eq(masterKey), Mockito.any(), Mockito.any(), Mockito.anyBoolean())
Mockito.doReturn(data.groupChange!!).`when`(groupsV2API).patchGroup(Mockito.any(), Mockito.any(), Mockito.any())
every { groupTable.getGroup(groupId) } returns data.groupRecord
every { groupTable.requireGroup(groupId) } returns data.groupRecord.get()
every { groupTable.update(any<GroupId.V2>(), any()) } returns Unit
every { sendGroupUpdateHelper.sendGroupUpdate(masterKey, any(), any(), any()) } returns GroupManagerV2.RecipientAndThread(Recipient.UNKNOWN, 1)
every { groupsV2API.patchGroup(any(), any(), any()) } returns data.groupChange!!
}
private fun editGroup(perform: GroupManagerV2.GroupEditor.() -> Unit) {
@@ -128,8 +124,9 @@ class GroupManagerV2Test_edit {
}
private fun then(then: (DecryptedGroup) -> Unit) {
Mockito.verify(groupTable).update(Mockito.eq(groupId), patchedDecryptedGroup.capture())
then(patchedDecryptedGroup.value)
val decryptedGroupArg = slot<DecryptedGroup>()
verify { groupTable.update(groupId, capture(decryptedGroupArg)) }
then(decryptedGroupArg.captured)
}
@Test

View File

@@ -2,6 +2,10 @@ package org.thoughtcrime.securesms.groups.v2.processing
import android.app.Application
import androidx.test.core.app.ApplicationProvider
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verify
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.both
import org.hamcrest.Matchers.hasItem
@@ -13,18 +17,6 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.ArgumentMatchers.anyLong
import org.mockito.ArgumentMatchers.eq
import org.mockito.Mockito.any
import org.mockito.Mockito.doCallRealMethod
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.isA
import org.mockito.Mockito.mock
import org.mockito.Mockito.reset
import org.mockito.Mockito.verify
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doNothing
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.signal.core.util.Hex.fromStringCondensed
@@ -48,6 +40,7 @@ import org.thoughtcrime.securesms.database.setNewTitle
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
import org.thoughtcrime.securesms.groups.GroupId
import org.thoughtcrime.securesms.groups.GroupsV2Authorization
import org.thoughtcrime.securesms.jobmanager.JobManager
import org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob
import org.thoughtcrime.securesms.logging.CustomSignalProtocolLogger
import org.thoughtcrime.securesms.testutil.SystemOutLogger
@@ -77,6 +70,7 @@ class GroupsV2StateProcessorTest {
private lateinit var groupsV2API: GroupsV2Api
private lateinit var groupsV2Authorization: GroupsV2Authorization
private lateinit var profileAndMessageHelper: GroupsV2StateProcessor.ProfileAndMessageHelper
private lateinit var jobManager: JobManager
private lateinit var processor: GroupsV2StateProcessor.StateProcessorForGroup
@@ -88,25 +82,29 @@ class GroupsV2StateProcessorTest {
Log.initialize(SystemOutLogger())
SignalProtocolLoggerProvider.setProvider(CustomSignalProtocolLogger())
groupTable = mock(GroupTable::class.java)
recipientTable = mock(RecipientTable::class.java)
groupsV2API = mock(GroupsV2Api::class.java)
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
profileAndMessageHelper = mock(GroupsV2StateProcessor.ProfileAndMessageHelper::class.java)
groupTable = mockk(relaxed = true)
recipientTable = mockk()
groupsV2API = mockk()
groupsV2Authorization = mockk(relaxed = true)
profileAndMessageHelper = mockk(relaxed = true)
jobManager = mockk(relaxed = true)
mockkStatic(ApplicationDependencies::class)
every { ApplicationDependencies.getJobManager() } returns jobManager
processor = GroupsV2StateProcessor.StateProcessorForGroup(serviceIds, ApplicationProvider.getApplicationContext(), groupTable, groupsV2API, groupsV2Authorization, masterKey, profileAndMessageHelper)
}
@After
fun tearDown() {
reset(ApplicationDependencies.getJobManager())
// reset(ApplicationDependencies.getJobManager())
}
private fun given(init: GroupStateTestData.() -> Unit) {
val data = givenData(init)
doReturn(data.groupRecord).`when`(groupTable).getGroup(any(GroupId.V2::class.java))
doReturn(!data.groupRecord.isPresent).`when`(groupTable).isUnknownGroup(any())
every { groupTable.getGroup(any<GroupId.V2>()) } returns data.groupRecord
every { groupTable.isUnknownGroup(any()) } returns !data.groupRecord.isPresent
data.serverState?.let { serverState ->
val testPartial = object : PartialDecryptedGroup(null, serverState, null, null) {
@@ -114,12 +112,13 @@ class GroupsV2StateProcessorTest {
return serverState
}
}
doReturn(testPartial).`when`(groupsV2API).getPartialDecryptedGroup(any(), any())
doReturn(serverState).`when`(groupsV2API).getGroup(any(), any())
every { groupsV2API.getPartialDecryptedGroup(any(), any()) } returns testPartial
every { groupsV2API.getGroup(any(), any()) } returns serverState
}
data.changeSet?.let { changeSet ->
doReturn(changeSet.toApiResponse()).`when`(groupsV2API).getGroupHistoryPage(any(), eq(data.requestedRevision), any(), eq(data.includeFirst))
every { groupsV2API.getGroupHistoryPage(any(), data.requestedRevision, any(), data.includeFirst) } returns changeSet.toApiResponse()
}
}
@@ -306,15 +305,14 @@ class GroupsV2StateProcessorTest {
apiCallParameters(2, true)
}
doReturn(true).`when`(groupTable).isUnknownGroup(any())
every { groupTable.isUnknownGroup(any()) } returns true
val result = processor.updateLocalGroupToRevision(2, 0, DecryptedGroupChange.getDefaultInstance())
assertThat("local should update to revision added", result.groupState, `is`(GroupsV2StateProcessor.GroupState.GROUP_UPDATED))
assertThat("revision matches peer revision added", result.latestServer!!.revision, `is`(2))
assertThat("title matches that as it was in revision added", result.latestServer!!.title, `is`("Baking Signal for Science"))
verify(ApplicationDependencies.getJobManager()).add(isA(RequestGroupV2InfoJob::class.java))
verify { jobManager.add(ofType(RequestGroupV2InfoJob::class)) }
}
@Test
@@ -406,7 +404,7 @@ class GroupsV2StateProcessorTest {
assertThat("local should update to server", result.groupState, `is`(GroupsV2StateProcessor.GroupState.GROUP_UPDATED))
assertThat("revision matches revision approved at", result.latestServer!!.revision, `is`(3))
assertThat("title matches revision approved at", result.latestServer!!.title, `is`("Beam me up"))
verify(ApplicationDependencies.getJobManager()).add(isA(RequestGroupV2InfoJob::class.java))
verify { jobManager.add(ofType(RequestGroupV2InfoJob::class)) }
}
@Test
@@ -458,7 +456,7 @@ class GroupsV2StateProcessorTest {
}
}
}
doReturn(secondApiCallChangeSet.changeSet!!.toApiResponse()).`when`(groupsV2API).getGroupHistoryPage(any(), eq(100), any(), eq(true))
every { groupsV2API.getGroupHistoryPage(any(), 100, any(), true) } returns secondApiCallChangeSet.changeSet!!.toApiResponse()
val result = processor.updateLocalGroupToRevision(GroupsV2StateProcessor.LATEST, 0, null)
@@ -474,10 +472,11 @@ class GroupsV2StateProcessorTest {
fun missedMemberAddResolvesWithMultipleRevisionUpdate() {
val secondOther = member(ServiceId.from(UUID.randomUUID()))
val updateMessageContextCapture = ArgumentCaptor.forClass(DecryptedGroupV2Context::class.java)
profileAndMessageHelper.masterKey = masterKey
doCallRealMethod().`when`(profileAndMessageHelper).insertUpdateMessages(anyLong(), anyOrNull(), any())
doNothing().`when`(profileAndMessageHelper).storeMessage(updateMessageContextCapture.capture(), anyLong())
val updateMessageContextArgs = mutableListOf<DecryptedGroupV2Context>()
every { profileAndMessageHelper.insertUpdateMessages(any(), any(), any()) } answers { callOriginal() }
every { profileAndMessageHelper.storeMessage(capture(updateMessageContextArgs), any()) } returns Unit
given {
localState(
@@ -513,8 +512,7 @@ class GroupsV2StateProcessorTest {
assertThat("local should update to server", result.groupState, `is`(GroupsV2StateProcessor.GroupState.GROUP_UPDATED))
assertThat("members contains second other", result.latestServer!!.membersList, hasItem(secondOther))
val allUpdateMessageContexts = updateMessageContextCapture.allValues
assertThat("group update messages contains new member add", allUpdateMessageContexts.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
assertThat("group update messages contains new member add", updateMessageContextArgs.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
}
/**
@@ -525,10 +523,11 @@ class GroupsV2StateProcessorTest {
fun missedMemberAddResolvesWithForcedUpdate() {
val secondOther = member(ServiceId.from(UUID.randomUUID()))
val updateMessageContextCapture = ArgumentCaptor.forClass(DecryptedGroupV2Context::class.java)
profileAndMessageHelper.masterKey = masterKey
doCallRealMethod().`when`(profileAndMessageHelper).insertUpdateMessages(anyLong(), anyOrNull(), any())
doNothing().`when`(profileAndMessageHelper).storeMessage(updateMessageContextCapture.capture(), anyLong())
val updateMessageContextArgs = mutableListOf<DecryptedGroupV2Context>()
every { profileAndMessageHelper.insertUpdateMessages(any(), any(), any()) } answers { callOriginal() }
every { profileAndMessageHelper.storeMessage(capture(updateMessageContextArgs), any()) } returns Unit
given {
localState(
@@ -548,12 +547,11 @@ class GroupsV2StateProcessorTest {
assertThat("members contains second other", result.latestServer!!.membersList, hasItem(secondOther))
assertThat("title should be updated", result.latestServer!!.title, `is`("Changed"))
val allUpdateMessageContexts = updateMessageContextCapture.allValues
assertThat("group update messages contains new member add", allUpdateMessageContexts.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
assertThat("group update messages contains new member add", updateMessageContextArgs.map { it.change.newMembersList }, hasItem(hasItem(secondOther)))
assertThat(
"group update messages contains title change",
allUpdateMessageContexts.map { it.change.newTitle },
updateMessageContextArgs.map { it.change.newTitle },
hasItem(both<DecryptedString>(notNullValue()).and(hasProperty("value", `is`("Changed"))))
)
}

View File

@@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.jobmanager.migrations;
import org.junit.Test;
import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.jobmanager.Data;
import org.thoughtcrime.securesms.jobmanager.JobMigration;
@@ -34,7 +35,7 @@ public class SenderKeyDistributionSendJobRecipientMigrationTest {
.putBlobAsString("group_id", GROUP_ID.getDecodedId())
.build());
GroupTable.GroupRecord mockGroup = mock(GroupTable.GroupRecord.class);
GroupRecord mockGroup = mock(GroupRecord.class);
when(mockGroup.getRecipientId()).thenReturn(RecipientId.from(2));
when(mockDatabase.getGroup(GROUP_ID)).thenReturn(Optional.of(mockGroup));