From ef374952abe740774f176628f01bbdff53a41da3 Mon Sep 17 00:00:00 2001 From: Clark Date: Thu, 7 Mar 2024 09:48:20 -0500 Subject: [PATCH] Add tests for update messages except for groups and calls. --- .../securesms/backup/v2/ImportExportTest.kt | 187 ++++++++++++++++++ .../v2/database/ChatItemExportIterator.kt | 16 +- .../v2/database/ChatItemImportInserter.kt | 33 ++-- .../database/MessageTableBackupExtensions.kt | 8 - 4 files changed, 217 insertions(+), 27 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt index e01508ba56..f1a0d7eb68 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/backup/v2/ImportExportTest.kt @@ -18,19 +18,25 @@ import org.thoughtcrime.securesms.backup.v2.proto.BodyRange import org.thoughtcrime.securesms.backup.v2.proto.Call import org.thoughtcrime.securesms.backup.v2.proto.Chat import org.thoughtcrime.securesms.backup.v2.proto.ChatItem +import org.thoughtcrime.securesms.backup.v2.proto.ChatUpdateMessage import org.thoughtcrime.securesms.backup.v2.proto.Contact import org.thoughtcrime.securesms.backup.v2.proto.DistributionList +import org.thoughtcrime.securesms.backup.v2.proto.ExpirationTimerChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.Frame import org.thoughtcrime.securesms.backup.v2.proto.Group +import org.thoughtcrime.securesms.backup.v2.proto.ProfileChangeChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.Quote import org.thoughtcrime.securesms.backup.v2.proto.Reaction import org.thoughtcrime.securesms.backup.v2.proto.Recipient import org.thoughtcrime.securesms.backup.v2.proto.ReleaseNotes import org.thoughtcrime.securesms.backup.v2.proto.Self import org.thoughtcrime.securesms.backup.v2.proto.SendStatus +import org.thoughtcrime.securesms.backup.v2.proto.SessionSwitchoverChatUpdate +import org.thoughtcrime.securesms.backup.v2.proto.SimpleChatUpdate import org.thoughtcrime.securesms.backup.v2.proto.StandardMessage import org.thoughtcrime.securesms.backup.v2.proto.StickerPack import org.thoughtcrime.securesms.backup.v2.proto.Text +import org.thoughtcrime.securesms.backup.v2.proto.ThreadMergeChatUpdate import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupReader import org.thoughtcrime.securesms.backup.v2.stream.EncryptedBackupWriter import org.thoughtcrime.securesms.keyvalue.SignalStore @@ -734,6 +740,187 @@ class ImportExportTest { compare(expected, exported) } + @Test + fun simpleChatUpdateMessage() { + var dateSentStart = 100L + val updateMessages = ArrayList() + for (i in 1..11) { + updateMessages.add( + ChatItem( + chatId = 1, + authorId = alice.id, + dateSent = dateSentStart++, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = dateSentStart, + dateServerSent = dateSentStart, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + simpleUpdate = SimpleChatUpdate( + type = SimpleChatUpdate.Type.fromValue(i)!! + ) + ) + ) + ) + } + importExport( + *standardFrames, + alice, + buildChat(alice, 1), + *updateMessages.toArray() + ) + } + + @Test + fun expirationTimerUpdateMessage() { + var dateSentStart = 100L + importExport( + *standardFrames, + alice, + buildChat(alice, 1), + ChatItem( + chatId = 1, + authorId = alice.id, + dateSent = dateSentStart++, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = dateSentStart, + dateServerSent = dateSentStart, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + expirationTimerChange = ExpirationTimerChatUpdate( + 1000 + ) + ) + ), + ChatItem( + chatId = 1, + authorId = selfRecipient.id, + dateSent = dateSentStart++, + outgoing = ChatItem.OutgoingMessageDetails( + sendStatus = listOf( + SendStatus(alice.id, deliveryStatus = SendStatus.Status.READ, sealedSender = true, lastStatusUpdateTimestamp = -1) + ) + ), + updateMessage = ChatUpdateMessage( + expirationTimerChange = ExpirationTimerChatUpdate( + 0 + ) + ) + ), + ChatItem( + chatId = 1, + authorId = selfRecipient.id, + dateSent = dateSentStart++, + outgoing = ChatItem.OutgoingMessageDetails( + sendStatus = listOf(SendStatus(alice.id, deliveryStatus = SendStatus.Status.READ, sealedSender = true, lastStatusUpdateTimestamp = -1)) + ), + updateMessage = ChatUpdateMessage( + expirationTimerChange = ExpirationTimerChatUpdate( + 10000 + ) + ) + ), + ChatItem( + chatId = 1, + authorId = alice.id, + dateSent = dateSentStart++, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = dateSentStart, + dateServerSent = dateSentStart, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + expirationTimerChange = ExpirationTimerChatUpdate( + 0 + ) + ) + ) + ) + } + + @Test + fun profileChangeChatUpdateMessage() { + var dateSentStart = 100L + importExport( + *standardFrames, + alice, + buildChat(alice, 1), + ChatItem( + chatId = 1, + authorId = alice.id, + dateSent = dateSentStart++, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = dateSentStart, + dateServerSent = dateSentStart, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + profileChange = ProfileChangeChatUpdate( + previousName = "Aliceee Kim", + newName = "Alice Kim" + ) + ) + ) + ) + } + + @Test + fun threadMergeChatUpdate() { + var dateSentStart = 100L + importExport( + *standardFrames, + alice, + buildChat(alice, 1), + ChatItem( + chatId = 1, + authorId = alice.id, + dateSent = dateSentStart++, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = dateSentStart, + dateServerSent = dateSentStart, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + threadMerge = ThreadMergeChatUpdate( + previousE164 = 141255501237 + ) + ) + ) + ) + } + + @Test + fun sessionSwitchoverChatUpdate() { + var dateSentStart = 100L + importExport( + *standardFrames, + alice, + buildChat(alice, 1), + ChatItem( + chatId = 1, + authorId = alice.id, + dateSent = dateSentStart++, + incoming = ChatItem.IncomingMessageDetails( + dateReceived = dateSentStart, + dateServerSent = dateSentStart, + read = true, + sealedSender = true + ), + updateMessage = ChatUpdateMessage( + sessionSwitchover = SessionSwitchoverChatUpdate( + e164 = 141255501237 + ) + ) + ) + ) + } + fun enumerateIncomingMessageDetails(dateSent: Long): List { val details = mutableListOf() details.add( diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt index b977ad34b8..a242b3ccb7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemExportIterator.kt @@ -117,14 +117,23 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: MessageTypes.isIdentityUpdate(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.IDENTITY_UPDATE)) MessageTypes.isIdentityVerified(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.IDENTITY_VERIFIED)) MessageTypes.isIdentityDefault(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.IDENTITY_DEFAULT)) - MessageTypes.isChangeNumber(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.CHANGE_NUMBER)) - MessageTypes.isBoostRequest(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.BOOST_REQUEST)) + MessageTypes.isChangeNumber(record.type) -> { + builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.CHANGE_NUMBER)) + builder.sms = false + } + MessageTypes.isBoostRequest(record.type) -> { + builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.BOOST_REQUEST)) + builder.sms = false + } MessageTypes.isEndSessionType(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.END_SESSION)) MessageTypes.isChatSessionRefresh(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.CHAT_SESSION_REFRESH)) MessageTypes.isBadDecryptType(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.BAD_DECRYPT)) MessageTypes.isPaymentsActivated(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.PAYMENTS_ACTIVATED)) MessageTypes.isPaymentsRequestToActivate(record.type) -> builder.updateMessage = ChatUpdateMessage(simpleUpdate = SimpleChatUpdate(type = SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST)) - MessageTypes.isExpirationTimerUpdate(record.type) -> builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate((record.expiresIn / 1000).toInt())) + MessageTypes.isExpirationTimerUpdate(record.type) -> { + builder.updateMessage = ChatUpdateMessage(expirationTimerChange = ExpirationTimerChatUpdate(record.expiresIn.toInt())) + builder.expiresInMs = null + } MessageTypes.isProfileChange(record.type) -> { builder.updateMessage = ChatUpdateMessage( profileChange = try { @@ -140,6 +149,7 @@ class ChatItemExportIterator(private val cursor: Cursor, private val batchSize: ProfileChangeChatUpdate() } ) + builder.sms = false } MessageTypes.isSessionSwitchoverType(record.type) -> { builder.updateMessage = ChatUpdateMessage( diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt index cfca33c537..6f3d46dba6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/ChatItemImportInserter.kt @@ -244,7 +244,7 @@ class ChatItemImportInserter( contentValues.put(MessageTable.TO_RECIPIENT_ID, (if (this.outgoing != null) chatRecipientId else selfId).serialize()) contentValues.put(MessageTable.THREAD_ID, threadId) contentValues.put(MessageTable.DATE_RECEIVED, this.incoming?.dateReceived ?: this.dateSent) - contentValues.put(MessageTable.RECEIPT_TIMESTAMP, this.outgoing?.sendStatus?.maxOf { it.lastStatusUpdateTimestamp } ?: 0) + contentValues.put(MessageTable.RECEIPT_TIMESTAMP, this.outgoing?.sendStatus?.maxOfOrNull { it.lastStatusUpdateTimestamp } ?: 0) contentValues.putNull(MessageTable.LATEST_REVISION_ID) contentValues.putNull(MessageTable.ORIGINAL_MESSAGE_ID) contentValues.put(MessageTable.REVISION_NUMBER, 0) @@ -382,23 +382,24 @@ class ChatItemImportInserter( var typeFlags: Long = 0 when { updateMessage.simpleUpdate != null -> { + val typeWithoutBase = (getAsLong(MessageTable.TYPE) and MessageTypes.BASE_TYPE_MASK.inv()) typeFlags = when (updateMessage.simpleUpdate.type) { - SimpleChatUpdate.Type.UNKNOWN -> 0 - SimpleChatUpdate.Type.JOINED_SIGNAL -> MessageTypes.JOINED_TYPE - SimpleChatUpdate.Type.IDENTITY_UPDATE -> MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT - SimpleChatUpdate.Type.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT - SimpleChatUpdate.Type.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT + SimpleChatUpdate.Type.UNKNOWN -> typeWithoutBase + SimpleChatUpdate.Type.JOINED_SIGNAL -> MessageTypes.JOINED_TYPE or typeWithoutBase + SimpleChatUpdate.Type.IDENTITY_UPDATE -> MessageTypes.KEY_EXCHANGE_IDENTITY_UPDATE_BIT or typeWithoutBase + SimpleChatUpdate.Type.IDENTITY_VERIFIED -> MessageTypes.KEY_EXCHANGE_IDENTITY_VERIFIED_BIT or typeWithoutBase + SimpleChatUpdate.Type.IDENTITY_DEFAULT -> MessageTypes.KEY_EXCHANGE_IDENTITY_DEFAULT_BIT or typeWithoutBase SimpleChatUpdate.Type.CHANGE_NUMBER -> MessageTypes.CHANGE_NUMBER_TYPE SimpleChatUpdate.Type.BOOST_REQUEST -> MessageTypes.BOOST_REQUEST_TYPE - SimpleChatUpdate.Type.END_SESSION -> MessageTypes.END_SESSION_BIT - SimpleChatUpdate.Type.CHAT_SESSION_REFRESH -> MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT - SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE - SimpleChatUpdate.Type.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED - SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST + SimpleChatUpdate.Type.END_SESSION -> MessageTypes.END_SESSION_BIT or typeWithoutBase + SimpleChatUpdate.Type.CHAT_SESSION_REFRESH -> MessageTypes.ENCRYPTION_REMOTE_FAILED_BIT or typeWithoutBase + SimpleChatUpdate.Type.BAD_DECRYPT -> MessageTypes.BAD_DECRYPT_TYPE or typeWithoutBase + SimpleChatUpdate.Type.PAYMENTS_ACTIVATED -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATED or typeWithoutBase + SimpleChatUpdate.Type.PAYMENT_ACTIVATION_REQUEST -> MessageTypes.SPECIAL_TYPE_PAYMENTS_ACTIVATE_REQUEST or typeWithoutBase } } updateMessage.expirationTimerChange != null -> { - typeFlags = MessageTypes.EXPIRATION_TIMER_UPDATE_BIT + typeFlags = getAsLong(MessageTable.TYPE) or MessageTypes.EXPIRATION_TIMER_UPDATE_BIT put(MessageTable.EXPIRES_IN, updateMessage.expirationTimerChange.expiresInMs.toLong()) } updateMessage.profileChange != null -> { @@ -408,12 +409,12 @@ class ChatItemImportInserter( put(MessageTable.BODY, Base64.encodeWithPadding(profileChangeDetails)) } updateMessage.sessionSwitchover != null -> { - typeFlags = MessageTypes.SESSION_SWITCHOVER_TYPE + typeFlags = MessageTypes.SESSION_SWITCHOVER_TYPE or (getAsLong(MessageTable.TYPE) and MessageTypes.BASE_TYPE_MASK.inv()) val sessionSwitchoverDetails = SessionSwitchoverEvent(e164 = updateMessage.sessionSwitchover.e164.toString()).encode() put(MessageTable.BODY, Base64.encodeWithPadding(sessionSwitchoverDetails)) } updateMessage.threadMerge != null -> { - typeFlags = MessageTypes.THREAD_MERGE_TYPE + typeFlags = MessageTypes.THREAD_MERGE_TYPE or (getAsLong(MessageTable.TYPE) and MessageTypes.BASE_TYPE_MASK.inv()) val threadMergeDetails = ThreadMergeEvent(previousE164 = updateMessage.threadMerge.previousE164.toString()).encode() put(MessageTable.BODY, Base64.encodeWithPadding(threadMergeDetails)) } @@ -448,10 +449,10 @@ class ChatItemImportInserter( GV2UpdateDescription(groupChangeUpdate = updateMessage.groupChange) ).encode() ) - typeFlags = MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT + typeFlags = getAsLong(MessageTable.TYPE) or MessageTypes.GROUP_V2_BIT or MessageTypes.GROUP_UPDATE_BIT } } - this.put(MessageTable.TYPE, getAsLong(MessageTable.TYPE) or typeFlags) + this.put(MessageTable.TYPE, typeFlags) } private fun ContentValues.addQuote(quote: Quote) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt index e08b820643..775423cdca 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/database/MessageTableBackupExtensions.kt @@ -54,14 +54,6 @@ fun MessageTable.getMessagesForBackup(backupTime: Long): ChatItemExportIterator .from(MessageTable.TABLE_NAME) .where( """ - ($BASE_TYPE IN ( - ${MessageTypes.BASE_INBOX_TYPE}, - ${MessageTypes.BASE_OUTBOX_TYPE}, - ${MessageTypes.BASE_SENT_TYPE}, - ${MessageTypes.BASE_SENDING_TYPE}, - ${MessageTypes.BASE_SENT_FAILED_TYPE} - ) OR ${MessageTable.IS_CALL_TYPE_CLAUSE}) - AND ( ${MessageTable.EXPIRE_STARTED} = 0 OR