mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-21 17:24:38 +01:00
Refactor archive requests into ArchiveService.
This commit is contained in:
committed by
Alex Hart
parent
1911bf7df7
commit
34648c2c04
+10
-1
@@ -9,7 +9,9 @@ import org.signal.core.util.billing.BillingApi
|
||||
import org.signal.libsignal.net.Network
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations
|
||||
import org.signal.network.api.ArchiveApi
|
||||
import org.signal.network.api.ArchiveApiV2
|
||||
import org.signal.network.config.SignalServiceConfiguration
|
||||
import org.signal.network.service.ArchiveService
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipientCache
|
||||
import org.thoughtcrime.securesms.testing.endpoints.DonationTestServer
|
||||
@@ -37,6 +39,7 @@ class InstrumentationApplicationDependencyProvider(val application: Application,
|
||||
private var signalServiceMessageSender: SignalServiceMessageSender? = null
|
||||
private var billingApi: BillingApi = mockk()
|
||||
private var accountApi: AccountApi = mockk()
|
||||
private var archiveService: ArchiveService = mockk(relaxed = true)
|
||||
|
||||
init {
|
||||
recipientCache = LiveRecipientCache(application) { r -> r.run() }
|
||||
@@ -50,10 +53,16 @@ class InstrumentationApplicationDependencyProvider(val application: Application,
|
||||
return recipientCache
|
||||
}
|
||||
|
||||
override fun provideArchiveApi(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket, pushServiceSocket: PushServiceSocket, signalServiceConfiguration: SignalServiceConfiguration): ArchiveApi {
|
||||
override fun provideArchiveApi(pushServiceSocket: PushServiceSocket): ArchiveApi {
|
||||
return mockk()
|
||||
}
|
||||
|
||||
override fun provideArchiveApiV2(authWebSocket: SignalWebSocket.AuthenticatedWebSocket, unauthWebSocket: SignalWebSocket.UnauthenticatedWebSocket, signalServiceConfiguration: SignalServiceConfiguration): ArchiveApiV2 {
|
||||
return mockk()
|
||||
}
|
||||
|
||||
override fun provideArchiveService(archiveApi: ArchiveApiV2): ArchiveService = archiveService
|
||||
|
||||
/**
|
||||
* Adds the Stripe-matching [ResponderInterceptor] on top of the default client (which supplies the
|
||||
* user agent + DNS), so `api.stripe.com` requests made by [org.signal.donations.StripeApi] are
|
||||
|
||||
+13
-15
@@ -6,11 +6,13 @@
|
||||
package org.thoughtcrime.securesms.jobs
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import arrow.core.right
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isNull
|
||||
import io.mockk.Runs
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.just
|
||||
import io.mockk.mockkObject
|
||||
@@ -24,7 +26,8 @@ import org.junit.runner.RunWith
|
||||
import org.signal.core.models.backup.MediaName
|
||||
import org.signal.core.models.database.AttachmentId
|
||||
import org.signal.core.util.Base64.decodeBase64OrThrow
|
||||
import org.signal.network.NetworkResult
|
||||
import org.signal.network.api.ArchiveApiV2
|
||||
import org.signal.network.service.ArchiveService
|
||||
import org.thoughtcrime.securesms.attachments.Attachment
|
||||
import org.thoughtcrime.securesms.attachments.PointerAttachment
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
@@ -33,12 +36,11 @@ import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.database.BackupMediaSnapshotTable.MediaEntry
|
||||
import org.thoughtcrime.securesms.database.MessageType
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.mms.IncomingMessage
|
||||
import org.thoughtcrime.securesms.testing.SignalActivityRule
|
||||
import org.thoughtcrime.securesms.util.MediaUtil
|
||||
import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse
|
||||
import org.whispersystems.signalservice.api.archive.ArchiveGetMediaItemsResponse.StoredMediaObject
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId
|
||||
import java.io.ByteArrayInputStream
|
||||
@@ -52,6 +54,8 @@ class ArchiveAttachmentReconciliationJobTest {
|
||||
@get:Rule
|
||||
val harness = SignalActivityRule()
|
||||
|
||||
private val archiveService: ArchiveService = AppDependencies.archiveService
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
||||
@@ -61,7 +65,7 @@ class ArchiveAttachmentReconciliationJobTest {
|
||||
|
||||
mockkObject(BackupRepository)
|
||||
mockkObject(ArchiveCommitAttachmentDeletesJob)
|
||||
every { ArchiveCommitAttachmentDeletesJob.deleteMediaObjectsFromCdn(any(), any(), any(), any()) } returns null
|
||||
coEvery { ArchiveCommitAttachmentDeletesJob.deleteMediaObjectsFromCdn(any(), any(), any(), any()) } returns null
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -221,20 +225,14 @@ class ArchiveAttachmentReconciliationJobTest {
|
||||
val remoteKey = attachment.remoteKey!!.decodeBase64OrThrow()
|
||||
val mediaId = MediaName.fromPlaintextHashAndRemoteKey(plaintextHash, remoteKey).toMediaId(SignalStore.backup.mediaRootBackupKey).encode()
|
||||
|
||||
every { BackupRepository.listRemoteMediaObjects(any(), any()) } returns NetworkResult.Success(
|
||||
ArchiveGetMediaItemsResponse(
|
||||
storedMediaObjects = listOf(StoredMediaObject(cdn = cdn, mediaId = mediaId, objectLength = attachment.size)),
|
||||
backupDir = null,
|
||||
mediaDir = null,
|
||||
cursor = null
|
||||
)
|
||||
)
|
||||
coEvery { archiveService.listRemoteMediaObjects(any(), any()) } returns ArchiveApiV2.MediaItemsPage(
|
||||
storedMediaObjects = listOf(ArchiveApiV2.StoredMediaObject(cdn = cdn, mediaId = mediaId, objectLength = attachment.size)),
|
||||
cursor = null
|
||||
).right()
|
||||
}
|
||||
|
||||
private fun fakeCdnEmpty() {
|
||||
every { BackupRepository.listRemoteMediaObjects(any(), any()) } returns NetworkResult.Success(
|
||||
ArchiveGetMediaItemsResponse(storedMediaObjects = emptyList(), backupDir = null, mediaDir = null, cursor = null)
|
||||
)
|
||||
coEvery { archiveService.listRemoteMediaObjects(any(), any()) } returns ArchiveApiV2.MediaItemsPage(storedMediaObjects = emptyList(), cursor = null).right()
|
||||
}
|
||||
|
||||
private fun createIncomingMessage(serverTime: Duration, attachment: Attachment): IncomingMessage {
|
||||
|
||||
@@ -5,11 +5,15 @@
|
||||
|
||||
package org.thoughtcrime.securesms.jobs
|
||||
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.contains
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isNull
|
||||
import assertk.assertions.isTrue
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.unmockkAll
|
||||
@@ -21,8 +25,9 @@ import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.Util
|
||||
import org.signal.network.NetworkResult
|
||||
import org.signal.network.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.signal.network.service.ArchiveError
|
||||
import org.signal.network.service.ArchiveService
|
||||
import org.thoughtcrime.securesms.attachments.Cdn
|
||||
import org.thoughtcrime.securesms.attachments.PointerAttachment
|
||||
import org.thoughtcrime.securesms.backup.DeletionState
|
||||
@@ -30,6 +35,7 @@ import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
import org.thoughtcrime.securesms.database.AttachmentTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobs.protos.BackupDeleteJobData
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.testing.Flag
|
||||
@@ -49,12 +55,14 @@ class BackupDeleteJobTest {
|
||||
@get:Rule
|
||||
val harness = SignalActivityRule()
|
||||
|
||||
private val archiveService: ArchiveService = AppDependencies.archiveService
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockkObject(BackupRepository)
|
||||
every { BackupRepository.getBackupTier() } returns NetworkResult.Success(MessageBackupTier.PAID)
|
||||
every { BackupRepository.deleteBackup() } returns NetworkResult.Success(Unit)
|
||||
every { BackupRepository.deleteMediaBackup() } returns NetworkResult.Success(Unit)
|
||||
every { BackupRepository.getBackupTier() } returns MessageBackupTier.PAID.right()
|
||||
coEvery { archiveService.deleteMessageBackup() } returns Unit.right()
|
||||
coEvery { archiveService.deleteMediaBackup() } returns Unit.right()
|
||||
}
|
||||
|
||||
@After
|
||||
@@ -193,9 +201,11 @@ class BackupDeleteJobTest {
|
||||
|
||||
val result = job.run()
|
||||
|
||||
coVerify {
|
||||
archiveService.deleteMessageBackup()
|
||||
archiveService.deleteMediaBackup()
|
||||
}
|
||||
verify {
|
||||
BackupRepository.deleteBackup()
|
||||
BackupRepository.deleteMediaBackup()
|
||||
BackupRepository.resetInitializedStateAndAuthCredentials()
|
||||
}
|
||||
|
||||
@@ -205,7 +215,7 @@ class BackupDeleteJobTest {
|
||||
|
||||
@Test
|
||||
fun givenNetworkErrorDuringMessageBackupDeletion_whenIRun_thenIExpectRetry() {
|
||||
every { BackupRepository.deleteBackup() } returns NetworkResult.NetworkError(IOException())
|
||||
coEvery { archiveService.deleteMessageBackup() } returns ArchiveError.NetworkError(IOException()).left()
|
||||
|
||||
SignalStore.backup.deletionState = DeletionState.CLEAR_LOCAL_STATE
|
||||
|
||||
@@ -218,7 +228,7 @@ class BackupDeleteJobTest {
|
||||
|
||||
@Test
|
||||
fun givenNetworkErrorDuringMediaBackupDeletion_whenIRun_thenIExpectRetry() {
|
||||
every { BackupRepository.deleteMediaBackup() } returns NetworkResult.NetworkError(IOException())
|
||||
coEvery { archiveService.deleteMediaBackup() } returns ArchiveError.NetworkError(IOException()).left()
|
||||
|
||||
SignalStore.backup.deletionState = DeletionState.CLEAR_LOCAL_STATE
|
||||
|
||||
@@ -231,7 +241,7 @@ class BackupDeleteJobTest {
|
||||
|
||||
@Test
|
||||
fun givenRateLimitedDuringMessageBackupDeletion_whenIRun_thenIExpectRetry() {
|
||||
every { BackupRepository.deleteBackup() } returns NetworkResult.StatusCodeError(NonSuccessfulResponseCodeException(429))
|
||||
coEvery { archiveService.deleteMessageBackup() } returns ArchiveError.CredentialError.RateLimited(null, NonSuccessfulResponseCodeException(429)).left()
|
||||
|
||||
SignalStore.backup.deletionState = DeletionState.CLEAR_LOCAL_STATE
|
||||
|
||||
@@ -244,7 +254,7 @@ class BackupDeleteJobTest {
|
||||
|
||||
@Test
|
||||
fun givenRateLimitedDuringMediaBackupDeletion_whenIRun_thenIExpectRetry() {
|
||||
every { BackupRepository.deleteMediaBackup() } returns NetworkResult.StatusCodeError(NonSuccessfulResponseCodeException(429))
|
||||
coEvery { archiveService.deleteMediaBackup() } returns ArchiveError.CredentialError.RateLimited(null, NonSuccessfulResponseCodeException(429)).left()
|
||||
|
||||
SignalStore.backup.deletionState = DeletionState.CLEAR_LOCAL_STATE
|
||||
|
||||
|
||||
+13
-19
@@ -6,6 +6,9 @@
|
||||
package org.thoughtcrime.securesms.jobs
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import arrow.core.Either
|
||||
import arrow.core.left
|
||||
import arrow.core.right
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isTrue
|
||||
@@ -29,6 +32,7 @@ import org.signal.core.util.money.FiatMoney
|
||||
import org.signal.donations.InAppPaymentType
|
||||
import org.signal.network.NetworkResult
|
||||
import org.signal.network.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.signal.network.service.ArchiveError
|
||||
import org.thoughtcrime.securesms.backup.DeletionState
|
||||
import org.thoughtcrime.securesms.backup.v2.BackupRepository
|
||||
import org.thoughtcrime.securesms.backup.v2.MessageBackupTier
|
||||
@@ -91,23 +95,9 @@ class BackupSubscriptionCheckJobTest {
|
||||
every { RecurringInAppPaymentRepository.ensureSubscriberIdSync(any(), any(), any()) } returns Unit
|
||||
|
||||
mockkObject(BackupRepository)
|
||||
every { BackupRepository.getBackupTier() } answers {
|
||||
val tier = SignalStore.backup.backupTier
|
||||
if (tier != null) {
|
||||
NetworkResult.Success(tier)
|
||||
} else {
|
||||
NetworkResult.StatusCodeError(NonSuccessfulResponseCodeException(404))
|
||||
}
|
||||
}
|
||||
every { BackupRepository.getBackupTier() } answers { currentTierResult() }
|
||||
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } answers {
|
||||
val tier = SignalStore.backup.backupTier
|
||||
if (tier != null) {
|
||||
NetworkResult.Success(tier)
|
||||
} else {
|
||||
NetworkResult.StatusCodeError(NonSuccessfulResponseCodeException(404))
|
||||
}
|
||||
}
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } answers { currentTierResult() }
|
||||
|
||||
every { BackupRepository.resetInitializedStateAndAuthCredentials() } returns Unit
|
||||
|
||||
@@ -464,7 +454,7 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
// Set up mismatched state: local tier is PAID but ZK tier is FREE
|
||||
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } returns NetworkResult.Success(MessageBackupTier.FREE)
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } returns MessageBackupTier.FREE.right()
|
||||
every { BackupRepository.resetInitializedStateAndAuthCredentials() } returns Unit
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
@@ -485,7 +475,7 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
// Set up synced state: both local and ZK tiers are PAID
|
||||
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } returns NetworkResult.Success(MessageBackupTier.PAID)
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } returns MessageBackupTier.PAID.right()
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
val result = job.run()
|
||||
@@ -504,7 +494,7 @@ class BackupSubscriptionCheckJobTest {
|
||||
|
||||
SignalStore.backup.backupTier = MessageBackupTier.PAID
|
||||
// ZK credential fetch fails, should trigger refresh
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } returns NetworkResult.StatusCodeError(NonSuccessfulResponseCodeException(500))
|
||||
every { BackupRepository.getBackupTierWithoutDowngrade() } returns ArchiveError.NetworkError(IOException("Server error: 500")).left()
|
||||
every { BackupRepository.resetInitializedStateAndAuthCredentials() } returns Unit
|
||||
|
||||
val job = BackupSubscriptionCheckJob.create()
|
||||
@@ -669,4 +659,8 @@ class BackupSubscriptionCheckJobTest {
|
||||
isAutoRenewing = false // Not auto-renewing means canceled
|
||||
)
|
||||
}
|
||||
|
||||
private fun currentTierResult(): Either<ArchiveError.CredentialError, MessageBackupTier> {
|
||||
return SignalStore.backup.backupTier?.right() ?: ArchiveError.CredentialError.NotFound(NonSuccessfulResponseCodeException(404)).left()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
package org.thoughtcrime.securesms.testing
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import org.json.JSONObject
|
||||
import org.junit.rules.ExternalResource
|
||||
import org.signal.libsignal.net.RequestResult
|
||||
import org.signal.network.NetworkResult
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.testing.endpoints.DonationResponses
|
||||
@@ -94,8 +96,8 @@ class InAppPaymentsRule : ExternalResource() {
|
||||
}
|
||||
|
||||
private fun initialiseSetArchiveBackupId() {
|
||||
AppDependencies.archiveApi.apply {
|
||||
every { triggerBackupIdReservation(any(), any(), any()) } returns NetworkResult.Success(Unit)
|
||||
AppDependencies.archiveApiV2.apply {
|
||||
coEvery { triggerBackupIdReservation(any(), any(), any()) } returns RequestResult.Success(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user