Ensure we are restoring media per spec with full media and thumbnail rules.

This commit is contained in:
Alex Hart
2025-07-14 15:56:41 -03:00
committed by Jeffrey Starke
parent 1137bbd8a5
commit 049e9460a0
4 changed files with 166 additions and 7 deletions

View File

@@ -6,24 +6,30 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.FlakyTest
import androidx.test.platform.app.InstrumentationRegistry
import assertk.assertThat
import assertk.assertions.hasSize
import assertk.assertions.isEqualTo
import assertk.assertions.isNotEqualTo
import assertk.assertions.isTrue
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNotEquals
import org.junit.Before
import org.junit.Ignore
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.signal.core.util.Base64.decodeBase64OrThrow
import org.signal.core.util.copyTo
import org.signal.core.util.stream.NullOutputStream
import org.thoughtcrime.securesms.attachments.ArchivedAttachment
import org.thoughtcrime.securesms.attachments.Attachment
import org.thoughtcrime.securesms.attachments.AttachmentId
import org.thoughtcrime.securesms.attachments.PointerAttachment
import org.thoughtcrime.securesms.attachments.UriAttachment
import org.thoughtcrime.securesms.mms.IncomingMessage
import org.thoughtcrime.securesms.mms.MediaStream
import org.thoughtcrime.securesms.mms.SentMediaQuality
import org.thoughtcrime.securesms.providers.BlobProvider
import org.thoughtcrime.securesms.testing.SignalActivityRule
import org.thoughtcrime.securesms.util.MediaUtil
import org.whispersystems.signalservice.api.crypto.AttachmentCipherOutputStream
import org.whispersystems.signalservice.api.crypto.NoCipherOutputStream
@@ -32,10 +38,19 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemo
import java.io.ByteArrayOutputStream
import java.io.File
import java.util.Optional
import java.util.UUID
import kotlin.random.Random
import kotlin.time.Duration
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds
@RunWith(AndroidJUnit4::class)
class AttachmentTableTest {
@get:Rule
val harness = SignalActivityRule(othersCount = 10)
@Before
fun setUp() {
SignalDatabase.attachments.deleteAllAttachments()
@@ -195,6 +210,57 @@ class AttachmentTableTest {
assertThat(SignalDatabase.attachments.getAttachment(attachmentId)!!.archiveTransferState).isEqualTo(AttachmentTable.ArchiveTransferState.NONE)
}
@Test
fun given10NewerAnd10OlderAttachments_whenIGetEachBatch_thenIExpectProperBucketing() {
val now = System.currentTimeMillis().milliseconds
val attachments = (0 until 20).map {
createArchivedAttachment()
}
val newMessages = attachments.take(10).mapIndexed { index, attachment ->
createIncomingMessage(serverTime = now - index.seconds, attachment = attachment)
}
val twoMonthsAgo = now - 60.days
val oldMessages = attachments.drop(10).mapIndexed { index, attachment ->
createIncomingMessage(serverTime = twoMonthsAgo - index.seconds, attachment = attachment)
}
(newMessages + oldMessages).forEach {
SignalDatabase.messages.insertMessageInbox(it)
}
val firstAttachmentsToDownload = SignalDatabase.attachments.getLast30DaysOfRestorableAttachments(500)
val nextAttachmentsToDownload = SignalDatabase.attachments.getOlderRestorableAttachments(500)
assertThat(firstAttachmentsToDownload).hasSize(10)
val resultNewMessages = SignalDatabase.messages.getMessages(firstAttachmentsToDownload.map { it.mmsId })
resultNewMessages.forEach {
assertThat(it.serverTimestamp.milliseconds >= now - 30.days).isTrue()
}
assertThat(nextAttachmentsToDownload).hasSize(10)
val resultOldMessages = SignalDatabase.messages.getMessages(nextAttachmentsToDownload.map { it.mmsId })
resultOldMessages.forEach {
assertThat(it.serverTimestamp.milliseconds < now - 30.days).isTrue()
}
}
private fun createIncomingMessage(
serverTime: Duration,
attachment: Attachment
): IncomingMessage {
return IncomingMessage(
type = MessageType.NORMAL,
from = harness.others[0],
body = null,
sentTimeMillis = serverTime.inWholeMilliseconds,
serverTimeMillis = serverTime.inWholeMilliseconds,
receivedTimeMillis = serverTime.inWholeMilliseconds,
attachments = listOf(attachment)
)
}
private fun createAttachmentPointer(key: ByteArray, digest: ByteArray, size: Int): Attachment {
return PointerAttachment.forPointer(
pointer = Optional.of(
@@ -223,6 +289,32 @@ class AttachmentTableTest {
).get()
}
private fun createArchivedAttachment(): Attachment {
return ArchivedAttachment(
contentType = "image/jpeg",
size = 1024,
cdn = 3,
uploadTimestamp = 0,
key = Random.nextBytes(8),
cdnKey = "password",
archiveCdn = 3,
plaintextHash = Random.nextBytes(8),
incrementalMac = Random.nextBytes(8),
incrementalMacChunkSize = 8,
width = 100,
height = 100,
caption = null,
blurHash = null,
voiceNote = false,
borderless = false,
stickerLocator = null,
gif = false,
quote = false,
uuid = UUID.randomUUID(),
fileName = null
)
}
private fun createAttachment(id: Long, uri: Uri, transformProperties: AttachmentTable.TransformProperties): UriAttachment {
return UriAttachmentBuilder.build(
id,