Add unit testing to story download enqueuer.
This commit is contained in:
parent
d2f639c57f
commit
13eb89746b
3 changed files with 300 additions and 0 deletions
|
@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
import net.zetetic.database.sqlcipher.SQLiteOpenHelper
|
import net.zetetic.database.sqlcipher.SQLiteOpenHelper
|
||||||
import org.signal.core.util.SqlUtil
|
import org.signal.core.util.SqlUtil
|
||||||
import org.signal.core.util.logging.Log
|
import org.signal.core.util.logging.Log
|
||||||
|
@ -221,6 +222,12 @@ open class SignalDatabase(private val context: Application, databaseSecret: Data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@VisibleForTesting
|
||||||
|
fun setSignalDatabaseInstanceForTesting(signalDatabase: SignalDatabase) {
|
||||||
|
this.instance = signalDatabase
|
||||||
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
val rawDatabase: net.zetetic.database.sqlcipher.SQLiteDatabase
|
val rawDatabase: net.zetetic.database.sqlcipher.SQLiteDatabase
|
||||||
get() = instance!!.rawWritableDatabase
|
get() = instance!!.rawWritableDatabase
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
package org.thoughtcrime.securesms.database
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||||
|
import org.thoughtcrime.securesms.attachments.DatabaseAttachment
|
||||||
|
import org.thoughtcrime.securesms.audio.AudioHash
|
||||||
|
import org.thoughtcrime.securesms.blurhash.BlurHash
|
||||||
|
import org.thoughtcrime.securesms.contactshare.Contact
|
||||||
|
import org.thoughtcrime.securesms.database.documents.IdentityKeyMismatch
|
||||||
|
import org.thoughtcrime.securesms.database.documents.NetworkFailure
|
||||||
|
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord
|
||||||
|
import org.thoughtcrime.securesms.database.model.ParentStoryId
|
||||||
|
import org.thoughtcrime.securesms.database.model.Quote
|
||||||
|
import org.thoughtcrime.securesms.database.model.ReactionRecord
|
||||||
|
import org.thoughtcrime.securesms.database.model.StoryType
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.BodyRangeList
|
||||||
|
import org.thoughtcrime.securesms.database.model.databaseprotos.GiftBadge
|
||||||
|
import org.thoughtcrime.securesms.linkpreview.LinkPreview
|
||||||
|
import org.thoughtcrime.securesms.mms.SlideDeck
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient
|
||||||
|
import org.thoughtcrime.securesms.stickers.StickerLocator
|
||||||
|
import org.thoughtcrime.securesms.util.MediaUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds MessageRecords and related components for direct usage in unit testing. Does not modify the database.
|
||||||
|
*/
|
||||||
|
object FakeMessageRecords {
|
||||||
|
|
||||||
|
fun buildDatabaseAttachment(
|
||||||
|
attachmentId: AttachmentId = AttachmentId(1, 1),
|
||||||
|
mmsId: Long = 1,
|
||||||
|
hasData: Boolean = true,
|
||||||
|
hasThumbnail: Boolean = true,
|
||||||
|
contentType: String = MediaUtil.IMAGE_JPEG,
|
||||||
|
transferProgress: Int = AttachmentDatabase.TRANSFER_PROGRESS_DONE,
|
||||||
|
size: Long = 0L,
|
||||||
|
fileName: String = "",
|
||||||
|
cdnNumber: Int = 1,
|
||||||
|
location: String = "",
|
||||||
|
key: String = "",
|
||||||
|
relay: String = "",
|
||||||
|
digest: ByteArray = byteArrayOf(),
|
||||||
|
fastPreflightId: String = "",
|
||||||
|
voiceNote: Boolean = false,
|
||||||
|
borderless: Boolean = false,
|
||||||
|
videoGif: Boolean = false,
|
||||||
|
width: Int = 0,
|
||||||
|
height: Int = 0,
|
||||||
|
quote: Boolean = false,
|
||||||
|
caption: String? = null,
|
||||||
|
stickerLocator: StickerLocator? = null,
|
||||||
|
blurHash: BlurHash? = null,
|
||||||
|
audioHash: AudioHash? = null,
|
||||||
|
transformProperties: AttachmentDatabase.TransformProperties? = null,
|
||||||
|
displayOrder: Int = 0,
|
||||||
|
uploadTimestamp: Long = 200
|
||||||
|
): DatabaseAttachment {
|
||||||
|
return DatabaseAttachment(
|
||||||
|
attachmentId,
|
||||||
|
mmsId,
|
||||||
|
hasData,
|
||||||
|
hasThumbnail,
|
||||||
|
contentType,
|
||||||
|
transferProgress,
|
||||||
|
size,
|
||||||
|
fileName,
|
||||||
|
cdnNumber,
|
||||||
|
location,
|
||||||
|
key,
|
||||||
|
relay,
|
||||||
|
digest,
|
||||||
|
fastPreflightId,
|
||||||
|
voiceNote,
|
||||||
|
borderless,
|
||||||
|
videoGif,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
quote,
|
||||||
|
caption,
|
||||||
|
stickerLocator,
|
||||||
|
blurHash,
|
||||||
|
audioHash,
|
||||||
|
transformProperties,
|
||||||
|
displayOrder,
|
||||||
|
uploadTimestamp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildLinkPreview(
|
||||||
|
url: String = "",
|
||||||
|
title: String = "",
|
||||||
|
description: String = "",
|
||||||
|
date: Long = 200,
|
||||||
|
attachmentId: AttachmentId? = null
|
||||||
|
): LinkPreview {
|
||||||
|
return LinkPreview(
|
||||||
|
url,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
date,
|
||||||
|
attachmentId
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun buildMediaMmsMessageRecord(
|
||||||
|
id: Long = 1,
|
||||||
|
conversationRecipient: Recipient = Recipient.UNKNOWN,
|
||||||
|
individualRecipient: Recipient = conversationRecipient,
|
||||||
|
recipientDeviceId: Int = 1,
|
||||||
|
dateSent: Long = 200,
|
||||||
|
dateReceived: Long = 400,
|
||||||
|
dateServer: Long = 300,
|
||||||
|
deliveryReceiptCount: Int = 0,
|
||||||
|
threadId: Long = 1,
|
||||||
|
body: String = "body",
|
||||||
|
slideDeck: SlideDeck = SlideDeck(),
|
||||||
|
partCount: Int = slideDeck.slides.count(),
|
||||||
|
mailbox: Long = MmsSmsColumns.Types.BASE_INBOX_TYPE,
|
||||||
|
mismatches: Set<IdentityKeyMismatch> = emptySet(),
|
||||||
|
failures: Set<NetworkFailure> = emptySet(),
|
||||||
|
subscriptionId: Int = -1,
|
||||||
|
expiresIn: Long = -1,
|
||||||
|
expireStarted: Long = -1,
|
||||||
|
viewOnce: Boolean = false,
|
||||||
|
readReceiptCount: Int = 0,
|
||||||
|
quote: Quote? = null,
|
||||||
|
contacts: List<Contact> = emptyList(),
|
||||||
|
linkPreviews: List<LinkPreview> = emptyList(),
|
||||||
|
unidentified: Boolean = false,
|
||||||
|
reactions: List<ReactionRecord> = emptyList(),
|
||||||
|
remoteDelete: Boolean = false,
|
||||||
|
mentionsSelf: Boolean = false,
|
||||||
|
notifiedTimestamp: Long = 350,
|
||||||
|
viewedReceiptCount: Int = 0,
|
||||||
|
receiptTimestamp: Long = 0,
|
||||||
|
messageRanges: BodyRangeList? = null,
|
||||||
|
storyType: StoryType = StoryType.NONE,
|
||||||
|
parentStoryId: ParentStoryId? = null,
|
||||||
|
giftBadge: GiftBadge? = null
|
||||||
|
): MediaMmsMessageRecord {
|
||||||
|
return MediaMmsMessageRecord(
|
||||||
|
id,
|
||||||
|
conversationRecipient,
|
||||||
|
individualRecipient,
|
||||||
|
recipientDeviceId,
|
||||||
|
dateSent,
|
||||||
|
dateReceived,
|
||||||
|
dateServer,
|
||||||
|
deliveryReceiptCount,
|
||||||
|
threadId,
|
||||||
|
body,
|
||||||
|
slideDeck,
|
||||||
|
partCount,
|
||||||
|
mailbox,
|
||||||
|
mismatches,
|
||||||
|
failures,
|
||||||
|
subscriptionId,
|
||||||
|
expiresIn,
|
||||||
|
expireStarted,
|
||||||
|
viewOnce,
|
||||||
|
readReceiptCount,
|
||||||
|
quote,
|
||||||
|
contacts,
|
||||||
|
linkPreviews,
|
||||||
|
unidentified,
|
||||||
|
reactions,
|
||||||
|
remoteDelete,
|
||||||
|
mentionsSelf,
|
||||||
|
notifiedTimestamp,
|
||||||
|
viewedReceiptCount,
|
||||||
|
receiptTimestamp,
|
||||||
|
messageRanges,
|
||||||
|
storyType,
|
||||||
|
parentStoryId,
|
||||||
|
giftBadge
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
package org.thoughtcrime.securesms.stories
|
||||||
|
|
||||||
|
import io.reactivex.rxjava3.plugins.RxJavaPlugins
|
||||||
|
import io.reactivex.rxjava3.schedulers.TestScheduler
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.MockedStatic
|
||||||
|
import org.mockito.junit.MockitoJUnit
|
||||||
|
import org.mockito.junit.MockitoRule
|
||||||
|
import org.mockito.kotlin.any
|
||||||
|
import org.mockito.kotlin.isA
|
||||||
|
import org.mockito.kotlin.never
|
||||||
|
import org.mockito.kotlin.verify
|
||||||
|
import org.mockito.kotlin.whenever
|
||||||
|
import org.thoughtcrime.securesms.attachments.AttachmentId
|
||||||
|
import org.thoughtcrime.securesms.database.AttachmentDatabase
|
||||||
|
import org.thoughtcrime.securesms.database.FakeMessageRecords
|
||||||
|
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||||
|
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||||
|
import org.thoughtcrime.securesms.jobmanager.JobManager
|
||||||
|
import org.thoughtcrime.securesms.jobs.AttachmentDownloadJob
|
||||||
|
|
||||||
|
class StoriesTest {
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
@JvmField
|
||||||
|
val mockitoRule: MockitoRule = MockitoJUnit.rule()
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var mockAttachmentDatabase: AttachmentDatabase
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var mockJobManager: JobManager
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var mockApplicationDependenciesStatic: MockedStatic<ApplicationDependencies>
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var mockSignalDatabaseStatic: MockedStatic<SignalDatabase>
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private lateinit var mockSignalDatabase: SignalDatabase
|
||||||
|
|
||||||
|
private val testScheduler = TestScheduler()
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
RxJavaPlugins.setInitIoSchedulerHandler { testScheduler }
|
||||||
|
RxJavaPlugins.setIoSchedulerHandler { testScheduler }
|
||||||
|
|
||||||
|
SignalDatabase.setSignalDatabaseInstanceForTesting(mockSignalDatabase)
|
||||||
|
whenever(SignalDatabase.attachments).thenReturn(mockAttachmentDatabase)
|
||||||
|
whenever(ApplicationDependencies.getJobManager()).thenReturn(mockJobManager)
|
||||||
|
whenever(mockAttachmentDatabase.getAttachmentsForMessage(any())).thenReturn(emptyList())
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
RxJavaPlugins.reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given a MessageRecord with no attachments and a LinkPreview without a thumbnail, when I enqueueAttachmentsFromStoryForDownload, then I enqueue nothing`() {
|
||||||
|
// GIVEN
|
||||||
|
val messageRecord = FakeMessageRecords.buildMediaMmsMessageRecord(
|
||||||
|
linkPreviews = listOf(FakeMessageRecords.buildLinkPreview())
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val testObserver = Stories.enqueueAttachmentsFromStoryForDownload(messageRecord, true).test()
|
||||||
|
testScheduler.triggerActions()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
testObserver.assertComplete()
|
||||||
|
verify(mockJobManager, never()).add(any())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given a MessageRecord with no attachments and a LinkPreview with a thumbnail, when I enqueueAttachmentsFromStoryForDownload, then I enqueue once`() {
|
||||||
|
// GIVEN
|
||||||
|
val messageRecord = FakeMessageRecords.buildMediaMmsMessageRecord(
|
||||||
|
linkPreviews = listOf(
|
||||||
|
FakeMessageRecords.buildLinkPreview(
|
||||||
|
attachmentId = AttachmentId(1, 2)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val testObserver = Stories.enqueueAttachmentsFromStoryForDownload(messageRecord, true).test()
|
||||||
|
testScheduler.triggerActions()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
testObserver.assertComplete()
|
||||||
|
verify(mockJobManager).add(isA<AttachmentDownloadJob>())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `Given a MessageRecord with an attachment, when I enqueueAttachmentsFromStoryForDownload, then I enqueue once`() {
|
||||||
|
// GIVEN
|
||||||
|
val attachment = FakeMessageRecords.buildDatabaseAttachment()
|
||||||
|
val messageRecord = FakeMessageRecords.buildMediaMmsMessageRecord()
|
||||||
|
whenever(mockAttachmentDatabase.getAttachmentsForMessage(any())).thenReturn(listOf(attachment))
|
||||||
|
|
||||||
|
// WHEN
|
||||||
|
val testObserver = Stories.enqueueAttachmentsFromStoryForDownload(messageRecord, true).test()
|
||||||
|
testScheduler.triggerActions()
|
||||||
|
|
||||||
|
// THEN
|
||||||
|
testObserver.assertComplete()
|
||||||
|
verify(mockJobManager).add(isA<AttachmentDownloadJob>())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue