diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTest.kt index e9bd146db8..800c0bec5e 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessorTest.kt @@ -5,8 +5,12 @@ import androidx.test.core.app.ApplicationProvider import org.junit.Rule import org.thoughtcrime.securesms.messages.MessageContentProcessor.ExceptionMetadata import org.thoughtcrime.securesms.messages.MessageContentProcessor.MessageState +import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.testing.SignalActivityRule +import org.thoughtcrime.securesms.testing.TestProtos import org.whispersystems.signalservice.api.messages.SignalServiceContent +import org.whispersystems.signalservice.internal.push.SignalServiceProtos +import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto abstract class MessageContentProcessorTest { @@ -28,4 +32,31 @@ abstract class MessageContentProcessorTest { return MessageContentProcessor.forNormalContent(context) } + + /** + * Creates a valid ServiceContentProto with a data message which can be built via + * `injectDataMessage`. This function is intended to be built on-top of for more + * specific scenario in subclasses. + * + * Example can be seen in __handleStoryMessageTest + */ + protected fun createServiceContentWithDataMessage( + messageSender: Recipient = Recipient.resolved(harness.others.first()), + injectDataMessage: SignalServiceProtos.DataMessage.Builder.() -> Unit + ): SignalServiceContentProto { + return TestProtos.build { + serviceContent( + localAddress = address(uuid = harness.self.requireServiceId().uuid()).build(), + metadata = metadata( + address = address(uuid = messageSender.requireServiceId().uuid()).build() + ).build() + ).apply { + content = content().apply { + dataMessage = dataMessage().apply { + injectDataMessage() + }.build() + }.build() + }.build() + } + } } diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleStoryMessageTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleStoryMessageTest.kt index 31cedcf7a4..bdc16b6d34 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleStoryMessageTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleStoryMessageTest.kt @@ -12,6 +12,7 @@ import org.signal.storageservice.protos.groups.local.DecryptedMember import org.thoughtcrime.securesms.database.MessageDatabase import org.thoughtcrime.securesms.database.MmsHelper import org.thoughtcrime.securesms.database.SignalDatabase +import org.thoughtcrime.securesms.database.model.DistributionListId import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord import org.thoughtcrime.securesms.database.model.ParentStoryId import org.thoughtcrime.securesms.database.model.StoryType @@ -20,6 +21,8 @@ import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.testing.TestProtos import org.thoughtcrime.securesms.util.FeatureFlagsTestUtil import org.whispersystems.signalservice.api.messages.SignalServiceContent +import org.whispersystems.signalservice.api.push.DistributionId +import org.whispersystems.signalservice.internal.push.SignalServiceProtos.DataMessage import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto import kotlin.random.Random @@ -37,6 +40,52 @@ class MessageContentProcessor__handleStoryMessageTest : MessageContentProcessorT SignalDatabase.mms.deleteAllThreads() } + @Test + fun givenContentWithADirectStoryReplyWhenIProcessThenIInsertAReplyInTheCorrectThread() { + val sender = Recipient.resolved(harness.others.first()) + val senderThreadId = SignalDatabase.threads.getOrCreateThreadIdFor(sender) + val myStory = Recipient.resolved(SignalDatabase.distributionLists.getRecipientId(DistributionListId.MY_STORY)!!) + val myStoryThread = SignalDatabase.threads.getOrCreateThreadIdFor(myStory) + val expectedSentTime = 200L + val storyMessageId = MmsHelper.insert( + sentTimeMillis = expectedSentTime, + recipient = myStory, + storyType = StoryType.STORY_WITH_REPLIES, + threadId = myStoryThread + ) + + SignalDatabase.storySends.insert( + messageId = storyMessageId, + recipientIds = listOf(sender.id), + sentTimestamp = expectedSentTime, + allowsReplies = true, + distributionId = DistributionId.MY_STORY + ) + + val expectedBody = "Hello!" + + val storyContent: SignalServiceContentProto = createServiceContentWithStoryContext( + messageSender = sender, + storyAuthor = harness.self, + storySentTimestamp = expectedSentTime + ) { + body = expectedBody + } + + runTestWithContent(contentProto = storyContent) + + val replyId = SignalDatabase.mmsSms.getConversation(senderThreadId, 0, 1).use { + it.moveToFirst() + it.requireLong(MessageDatabase.ID) + } + + val replyRecord = SignalDatabase.mms.getMessageRecord(replyId) as MediaMmsMessageRecord + assertEquals(ParentStoryId.DirectReply(storyMessageId).serialize(), replyRecord.parentStoryId!!.serialize()) + assertEquals(expectedBody, replyRecord.body) + + SignalDatabase.mms.deleteAllThreads() + } + @Test fun givenContentWithAGroupStoryReplyWhenIProcessThenIInsertAReplyToTheCorrectStory() { val sender = Recipient.resolved(harness.others[0]) @@ -79,25 +128,13 @@ class MessageContentProcessor__handleStoryMessageTest : MessageContentProcessorT ) val expectedBody = "Hello, World!" - val storyContent: SignalServiceContentProto = TestProtos.build { - serviceContent( - localAddress = address(uuid = harness.self.requireServiceId().uuid()).build(), - metadata = metadata( - address = address(uuid = sender.requireServiceId().uuid()).build() - ).build() - ).apply { - content = content().apply { - dataMessage = dataMessage().apply { - storyContext = storyContext( - sentTimestamp = 100L, - authorUuid = sender.requireServiceId().toString() - ).build() - - groupV2 = groupContextV2(masterKeyBytes = groupMasterKey.serialize()).build() - body = expectedBody - }.build() - }.build() - }.build() + val storyContent: SignalServiceContentProto = createServiceContentWithStoryContext( + messageSender = sender, + storyAuthor = sender, + storySentTimestamp = 100L + ) { + groupV2 = TestProtos.build { groupContextV2(masterKeyBytes = groupMasterKey.serialize()).build() } + body = expectedBody } runTestWithContent(storyContent) @@ -114,6 +151,28 @@ class MessageContentProcessor__handleStoryMessageTest : MessageContentProcessorT assertEquals(expectedBody, replyRecord.body) SignalDatabase.mms.deleteGroupStoryReplies(insertResult.get().messageId) + SignalDatabase.mms.deleteAllThreads() + } + + /** + * Creates a ServiceContent proto with a StoryContext, and then + * uses `injectDataMessage` to fill in the data message object. + */ + private fun createServiceContentWithStoryContext( + messageSender: Recipient, + storyAuthor: Recipient, + storySentTimestamp: Long, + injectDataMessage: DataMessage.Builder.() -> Unit + ): SignalServiceContentProto { + return createServiceContentWithDataMessage(messageSender) { + storyContext = TestProtos.build { + storyContext( + sentTimestamp = storySentTimestamp, + authorUuid = storyAuthor.requireServiceId().toString() + ).build() + } + injectDataMessage() + } } private fun runTestWithContent(contentProto: SignalServiceContentProto) { diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleTextMessageTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleTextMessageTest.kt index 3d0e9c5ea1..38552ce6f7 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleTextMessageTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/messages/MessageContentProcessor__handleTextMessageTest.kt @@ -4,7 +4,6 @@ import org.junit.Assert.assertEquals import org.junit.Assert.assertTrue import org.junit.Test import org.thoughtcrime.securesms.database.SignalDatabase -import org.thoughtcrime.securesms.testing.TestProtos import org.whispersystems.signalservice.api.messages.SignalServiceContent import org.whispersystems.signalservice.internal.serialize.protos.SignalServiceContentProto @@ -14,11 +13,9 @@ class MessageContentProcessor__handleTextMessageTest : MessageContentProcessorTe fun givenContentWithATextMessageWhenIProcessThenIInsertTheTextMessage() { val testSubject: MessageContentProcessor = createNormalContentTestSubject() val expectedBody = "Hello, World!" - val contentProto: SignalServiceContentProto = TestProtos.build { - val dataMessage = dataMessage().apply { body = expectedBody } - val content = content().apply { this.dataMessage = dataMessage.build() } - serviceContent().apply { this.content = content.build() } - }.build() + val contentProto: SignalServiceContentProto = createServiceContentWithDataMessage { + body = expectedBody + } val content = SignalServiceContent.createFromProto(contentProto)