mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-09-20 08:45:50 +01:00
Add tests for AppRegistrationStorageController.
This commit is contained in:
@@ -133,6 +133,14 @@ class SignalStore(context: Application, private val store: KeyValueStore) {
|
||||
instance!!.store.resetCache()
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps out the singleton for a test instance. Pass null to clear it. Should only be used for testing!
|
||||
*/
|
||||
@VisibleForTesting
|
||||
fun testInject(instance: SignalStore?) {
|
||||
Companion.instance = instance
|
||||
}
|
||||
|
||||
/**
|
||||
* Restoring a backup changes the underlying disk values, so the cache needs to be reset.
|
||||
*/
|
||||
|
||||
+14
-4
@@ -8,6 +8,7 @@ package org.thoughtcrime.securesms.registration.v2
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.core.net.toUri
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
@@ -104,6 +105,16 @@ import kotlin.time.Duration.Companion.seconds
|
||||
*/
|
||||
class AppRegistrationStorageController(private val context: Context) : StorageController {
|
||||
|
||||
/**
|
||||
* Restarts the process-wide network stack after account data is applied. Overridable only so tests can avoid
|
||||
* touching the real, suite-shared network module; production must never replace it.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
internal var restartNetwork: () -> Unit = {
|
||||
AppDependencies.resetNetwork()
|
||||
AppDependencies.startNetwork()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val TAG = Log.tag(AppRegistrationStorageController::class)
|
||||
private const val TEMP_PROTO_FILENAME = "registration-in-progress.proto"
|
||||
@@ -689,7 +700,7 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo
|
||||
|
||||
val profileKey = getOrCreateProfileKey(accountData.e164)
|
||||
val recipientTable = SignalDatabase.recipients
|
||||
val selfId = Recipient.trustedPush(aci, pni, accountData.e164).id
|
||||
val selfId = recipientTable.getAndPossiblyMergePnpVerified(aci, pni, accountData.e164)
|
||||
|
||||
recipientTable.setProfileSharing(selfId, true)
|
||||
recipientTable.markRegisteredOrThrow(selfId, aci)
|
||||
@@ -722,8 +733,7 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo
|
||||
restoredAEP = SignalStore.account.restoredAccountEntropyPool
|
||||
)
|
||||
|
||||
AppDependencies.resetNetwork()
|
||||
AppDependencies.startNetwork()
|
||||
restartNetwork()
|
||||
PreKeysSyncJob.enqueue()
|
||||
|
||||
recipientTable.clearSelfKeyTransparencyData()
|
||||
@@ -749,7 +759,7 @@ class AppRegistrationStorageController(private val context: Context) : StorageCo
|
||||
}
|
||||
|
||||
private fun getOrCreateProfileKey(e164: String): ProfileKey {
|
||||
val existing = SignalDatabase.recipients.getByE164(e164).getOrNull()?.let { ProfileKeyUtil.profileKeyOrNull(Recipient.resolved(it).profileKey) }
|
||||
val existing = SignalDatabase.recipients.getByE164(e164).getOrNull()?.let { ProfileKeyUtil.profileKeyOrNull(SignalDatabase.recipients.getRecord(it).profileKey) }
|
||||
return existing ?: ProfileKeyUtil.createNew().also { Log.i(TAG, "[commitRegistrationData] No profile key found, created a new one") }
|
||||
}
|
||||
|
||||
|
||||
+416
@@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.registration.v2
|
||||
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isGreaterThan
|
||||
import assertk.assertions.isNotNull
|
||||
import assertk.assertions.isNull
|
||||
import assertk.assertions.isTrue
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.justRun
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkAll
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import okio.ByteString.Companion.toByteString
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.signal.core.models.AccountEntropyPool
|
||||
import org.signal.core.models.MasterKey
|
||||
import org.signal.core.models.ServiceId.ACI
|
||||
import org.signal.core.models.ServiceId.PNI
|
||||
import org.signal.core.util.contentproviders.BlobProvider
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.registration.proto.AccountData
|
||||
import org.signal.registration.proto.LinkedDeviceData
|
||||
import org.signal.registration.proto.RegistrationData
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
||||
import org.thoughtcrime.securesms.database.RecipientTable
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase
|
||||
import org.thoughtcrime.securesms.dependencies.AppDependencies
|
||||
import org.thoughtcrime.securesms.jobmanager.runJobBlocking
|
||||
import org.thoughtcrime.securesms.jobs.DirectoryRefreshJob
|
||||
import org.thoughtcrime.securesms.jobs.PreKeysSyncJob
|
||||
import org.thoughtcrime.securesms.jobs.RefreshOwnProfileJob
|
||||
import org.thoughtcrime.securesms.jobs.RotateCertificateJob
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.registration.util.RegistrationUtil
|
||||
import org.thoughtcrime.securesms.testutil.MockAppDependenciesRule
|
||||
import org.thoughtcrime.securesms.testutil.SignalDatabaseRule
|
||||
import org.thoughtcrime.securesms.testutil.SignalStoreRule
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.util.UUID
|
||||
import kotlin.random.Random
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = Application::class, manifest = Config.NONE)
|
||||
class AppRegistrationStorageControllerTest {
|
||||
|
||||
companion object {
|
||||
private const val E164 = "+15555550101"
|
||||
private const val SERVICE_PASSWORD = "service-password"
|
||||
private const val PIN = "1234"
|
||||
}
|
||||
|
||||
@get:Rule
|
||||
val signalStore = SignalStoreRule()
|
||||
|
||||
@get:Rule
|
||||
val appDependencies = MockAppDependenciesRule()
|
||||
|
||||
@get:Rule
|
||||
val signalDatabase = SignalDatabaseRule()
|
||||
|
||||
private val context: Application = ApplicationProvider.getApplicationContext()
|
||||
|
||||
private val aci = ACI.from(UUID.randomUUID())
|
||||
private val pni = PNI.from(UUID.randomUUID())
|
||||
private val aep = AccountEntropyPool.generate()
|
||||
private val aciIdentity = IdentityKeyPair.generate()
|
||||
private val pniIdentity = IdentityKeyPair.generate()
|
||||
private val aciSignedPreKey = PreKeyUtil.generateSignedPreKey(11, aciIdentity.privateKey)
|
||||
private val pniSignedPreKey = PreKeyUtil.generateSignedPreKey(12, pniIdentity.privateKey)
|
||||
private val aciLastResortKyberPreKey = PreKeyUtil.generateLastResortKyberPreKey(21, aciIdentity.privateKey)
|
||||
private val pniLastResortKyberPreKey = PreKeyUtil.generateLastResortKyberPreKey(22, pniIdentity.privateKey)
|
||||
|
||||
private val blobData = mutableMapOf<Uri, ByteArray>()
|
||||
private var blobCounter = 0
|
||||
|
||||
private lateinit var controller: AppRegistrationStorageController
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockkStatic(RegistrationUtil::class)
|
||||
justRun { RegistrationUtil.maybeMarkRegistrationComplete() }
|
||||
|
||||
mockkStatic("org.thoughtcrime.securesms.jobmanager.JobManagerExtensionsKt")
|
||||
coEvery { AppDependencies.jobManager.runJobBlocking(any(), any()) } returns null
|
||||
|
||||
stubInMemoryBlobs()
|
||||
|
||||
controller = AppRegistrationStorageController(context)
|
||||
|
||||
// The real network restart tears down the suite-shared network module, whose CompositeDisposable can hold
|
||||
// leaked mocks from other tests. It sets no observable state, so a no-op is safe here.
|
||||
controller.restartNetwork = {}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
unmockkAll()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `commit - new primary with pin and aep - applies all account fields`() = runBlocking<Unit> {
|
||||
seedInProgressData(
|
||||
RegistrationData(
|
||||
accountData = accountData(),
|
||||
accountEntropyPool = aep.value,
|
||||
pin = PIN,
|
||||
registrationLockEnabled = true
|
||||
)
|
||||
)
|
||||
|
||||
controller.commitRegistrationData()
|
||||
|
||||
assertThat(SignalStore.account.aci).isEqualTo(aci)
|
||||
assertThat(SignalStore.account.pni).isEqualTo(pni)
|
||||
assertThat(SignalStore.account.e164).isEqualTo(E164)
|
||||
assertThat(SignalStore.account.servicePassword).isEqualTo(SERVICE_PASSWORD)
|
||||
assertThat(SignalStore.account.registrationId).isEqualTo(12345)
|
||||
assertThat(SignalStore.account.pniRegistrationId).isEqualTo(54321)
|
||||
assertThat(SignalStore.account.isRegistered).isTrue()
|
||||
assertThat(SignalStore.account.isMultiDevice).isFalse()
|
||||
assertThat(SignalStore.account.aciIdentityKey.serialize()).isEqualTo(aciIdentity.serialize())
|
||||
assertThat(SignalStore.account.pniIdentityKey.serialize()).isEqualTo(pniIdentity.serialize())
|
||||
assertThat(SignalStore.account.accountEntropyPool.value).isEqualTo(aep.value)
|
||||
assertThat(SignalStore.account.restoredAccountEntropyPool).isTrue()
|
||||
|
||||
assertThat(SignalStore.account.aciPreKeys.isSignedPreKeyRegistered).isTrue()
|
||||
assertThat(SignalStore.account.aciPreKeys.activeSignedPreKeyId).isEqualTo(11)
|
||||
assertThat(SignalStore.account.aciPreKeys.lastResortKyberPreKeyId).isEqualTo(21)
|
||||
assertThat(SignalStore.account.pniPreKeys.isSignedPreKeyRegistered).isTrue()
|
||||
assertThat(SignalStore.account.pniPreKeys.activeSignedPreKeyId).isEqualTo(12)
|
||||
assertThat(SignalStore.account.pniPreKeys.lastResortKyberPreKeyId).isEqualTo(22)
|
||||
|
||||
assertThat(SignalStore.svr.pin).isEqualTo(PIN)
|
||||
assertThat(SignalStore.svr.isRegistrationLockEnabled).isTrue()
|
||||
assertThat(SignalStore.svr.hasOptedOut()).isFalse()
|
||||
|
||||
val selfId = SignalDatabase.recipients.getByAci(aci).get()
|
||||
val selfRecord = SignalDatabase.recipients.getRecord(selfId)
|
||||
assertThat(selfRecord.profileSharing).isTrue()
|
||||
assertThat(selfRecord.registered).isEqualTo(RecipientTable.RegisteredState.REGISTERED)
|
||||
assertThat(selfRecord.e164).isEqualTo(E164)
|
||||
assertThat(selfRecord.pni).isEqualTo(pni)
|
||||
assertThat(selfRecord.profileKey).isNotNull()
|
||||
|
||||
assertThat(TextSecurePreferences.hasPromptedPushRegistration(context)).isTrue()
|
||||
assertThat(TextSecurePreferences.isUnauthorizedReceived(context)).isFalse()
|
||||
|
||||
assertThat(readInProgressData().accountDataCommitted).isTrue()
|
||||
|
||||
verify { AppDependencies.jobManager.add(ofType<PreKeysSyncJob>()) }
|
||||
verify { AppDependencies.jobManager.add(ofType<DirectoryRefreshJob>()) }
|
||||
verify { AppDependencies.jobManager.add(ofType<RotateCertificateJob>()) }
|
||||
verify { RegistrationUtil.maybeMarkRegistrationComplete() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `commit - linked device - applies linked device fields`() = runBlocking<Unit> {
|
||||
val mediaRootBackupKey = Random.nextBytes(32)
|
||||
|
||||
seedInProgressData(
|
||||
RegistrationData(
|
||||
accountData = accountData(
|
||||
linkedDeviceData = LinkedDeviceData(
|
||||
deviceId = 2,
|
||||
deviceName = "device-name",
|
||||
mediaRootBackupKey = mediaRootBackupKey.toByteString(),
|
||||
readReceipts = true
|
||||
)
|
||||
),
|
||||
accountEntropyPool = aep.value
|
||||
)
|
||||
)
|
||||
|
||||
controller.commitRegistrationData()
|
||||
|
||||
assertThat(SignalStore.account.deviceId).isEqualTo(2)
|
||||
assertThat(SignalStore.account.deviceName).isEqualTo("device-name")
|
||||
assertThat(SignalStore.account.isMultiDevice).isTrue()
|
||||
assertThat(SignalStore.account.isRegistered).isTrue()
|
||||
assertThat(SignalStore.account.accountEntropyPool.value).isEqualTo(aep.value)
|
||||
assertThat(SignalStore.account.restoredAccountEntropyPool).isFalse()
|
||||
assertThat(SignalStore.account.restoredAccountEntropyPoolFromPrimary).isTrue()
|
||||
assertThat(SignalStore.backup.mediaRootBackupKey.value).isEqualTo(mediaRootBackupKey)
|
||||
assertThat(TextSecurePreferences.isReadReceiptsEnabled(context)).isTrue()
|
||||
|
||||
assertThat(SignalStore.svr.pin).isNull()
|
||||
assertThat(SignalStore.svr.hasOptedOut()).isFalse()
|
||||
|
||||
coVerify { AppDependencies.jobManager.runJobBlocking(ofType<RefreshOwnProfileJob>(), any()) }
|
||||
verify { AppDependencies.jobManager.add(ofType<RotateCertificateJob>()) }
|
||||
verify(exactly = 0) { AppDependencies.jobManager.add(ofType<DirectoryRefreshJob>()) }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `commit - pin opted out - applies svr opt out`() = runBlocking<Unit> {
|
||||
seedInProgressData(
|
||||
RegistrationData(
|
||||
accountData = accountData(),
|
||||
pinOptedOut = true
|
||||
)
|
||||
)
|
||||
|
||||
controller.commitRegistrationData()
|
||||
|
||||
assertThat(SignalStore.account.isRegistered).isTrue()
|
||||
assertThat(SignalStore.svr.pin).isNull()
|
||||
assertThat(SignalStore.svr.hasOptedOut()).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `commit - called twice - only applies account data once`() = runBlocking<Unit> {
|
||||
seedInProgressData(
|
||||
RegistrationData(
|
||||
accountData = accountData(),
|
||||
accountEntropyPool = aep.value,
|
||||
pin = PIN
|
||||
)
|
||||
)
|
||||
|
||||
controller.commitRegistrationData()
|
||||
controller.commitRegistrationData()
|
||||
|
||||
assertThat(readInProgressData().accountDataCommitted).isTrue()
|
||||
assertThat(SignalStore.svr.pin).isEqualTo(PIN)
|
||||
|
||||
verify(exactly = 1) { AppDependencies.jobManager.add(ofType<PreKeysSyncJob>()) }
|
||||
verify(exactly = 2) { RegistrationUtil.maybeMarkRegistrationComplete() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `commit - incomplete account data - applies nothing`() = runBlocking<Unit> {
|
||||
seedInProgressData(
|
||||
RegistrationData(
|
||||
accountData = accountData(servicePassword = "")
|
||||
)
|
||||
)
|
||||
|
||||
controller.commitRegistrationData()
|
||||
|
||||
assertThat(SignalStore.account.isRegistered).isFalse()
|
||||
assertThat(SignalStore.svr.pin).isNull()
|
||||
assertThat(SignalStore.svr.hasOptedOut()).isFalse()
|
||||
assertThat(readInProgressData().accountDataCommitted).isFalse()
|
||||
|
||||
verify(exactly = 0) { AppDependencies.jobManager.add(ofType<PreKeysSyncJob>()) }
|
||||
verify { RegistrationUtil.maybeMarkRegistrationComplete() }
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `commit - master key for initial data restore - is applied last and survives svr updates`() = runBlocking<Unit> {
|
||||
val initialRestoreKey = MasterKey(Random.nextBytes(32))
|
||||
|
||||
seedInProgressData(
|
||||
RegistrationData(
|
||||
accountData = accountData(),
|
||||
accountEntropyPool = aep.value,
|
||||
pin = PIN,
|
||||
masterKeyForInitialDataRestore = initialRestoreKey.serialize().toByteString()
|
||||
)
|
||||
)
|
||||
|
||||
controller.commitRegistrationData()
|
||||
|
||||
assertThat(SignalStore.svr.masterKeyForInitialDataRestore!!.serialize()).isEqualTo(initialRestoreKey.serialize())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `readInProgressRegistrationData - returns persisted data`() = runBlocking<Unit> {
|
||||
val data = RegistrationData(pin = PIN, accountEntropyPool = aep.value)
|
||||
seedInProgressData(data)
|
||||
|
||||
val read = controller.readInProgressRegistrationData()
|
||||
|
||||
assertThat(read).isEqualTo(data)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `readInProgressRegistrationData - no data - returns empty`() = runBlocking<Unit> {
|
||||
val read = controller.readInProgressRegistrationData()
|
||||
|
||||
assertThat(read).isEqualTo(RegistrationData())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `updateInProgressRegistrationData - applies updater, persists, and deletes previous blob`() = runBlocking<Unit> {
|
||||
seedInProgressData(RegistrationData(accountEntropyPool = aep.value))
|
||||
|
||||
controller.updateInProgressRegistrationData { pin = PIN }
|
||||
|
||||
val read = readInProgressData()
|
||||
assertThat(read.pin).isEqualTo(PIN)
|
||||
assertThat(read.accountEntropyPool).isEqualTo(aep.value)
|
||||
assertThat(read.lastUpdatedMillis).isGreaterThan(0L)
|
||||
assertThat(blobData.size).isEqualTo(1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `clearAllData - deletes blob and clears uri`() = runBlocking<Unit> {
|
||||
seedInProgressData(RegistrationData(pin = PIN))
|
||||
val legacyFile = File(context.cacheDir, "registration-in-progress.proto").apply { createNewFile() }
|
||||
|
||||
controller.clearAllData()
|
||||
|
||||
assertThat(SignalStore.registration.inProgressRegistrationDataBlobUri).isNull()
|
||||
assertThat(blobData.size).isEqualTo(0)
|
||||
assertThat(legacyFile.exists()).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPreExistingRegistrationData - not registered - returns null`() = runBlocking<Unit> {
|
||||
val data = controller.getPreExistingRegistrationData()
|
||||
|
||||
assertThat(data).isNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getPreExistingRegistrationData - registered - returns account state`() = runBlocking<Unit> {
|
||||
SignalStore.account.setAci(aci)
|
||||
SignalStore.account.setPni(pni)
|
||||
SignalStore.account.setE164(E164)
|
||||
SignalStore.account.setServicePassword(SERVICE_PASSWORD)
|
||||
SignalStore.account.restoreAccountEntropyPool(aep)
|
||||
SignalStore.account.restoreAciIdentityKeyFromBackup(aciIdentity.publicKey.serialize(), aciIdentity.privateKey.serialize())
|
||||
SignalStore.account.restorePniIdentityKeyFromBackup(pniIdentity.publicKey.serialize(), pniIdentity.privateKey.serialize())
|
||||
SignalStore.account.setRegistered(true)
|
||||
SignalStore.svr.isRegistrationLockEnabled = true
|
||||
|
||||
val data = controller.getPreExistingRegistrationData()!!
|
||||
|
||||
assertThat(data.e164).isEqualTo(E164)
|
||||
assertThat(data.aci).isEqualTo(aci)
|
||||
assertThat(data.pni).isEqualTo(pni)
|
||||
assertThat(data.servicePassword).isEqualTo(SERVICE_PASSWORD)
|
||||
assertThat(data.aep.value).isEqualTo(aep.value)
|
||||
assertThat(data.registrationLockEnabled).isTrue()
|
||||
assertThat(data.aciIdentityKeyPair.serialize()).isEqualTo(aciIdentity.serialize())
|
||||
assertThat(data.pniIdentityKeyPair.serialize()).isEqualTo(pniIdentity.serialize())
|
||||
}
|
||||
|
||||
/**
|
||||
* Backs the relaxed [AppDependencies.blobs] mock with an in-memory map so the controller's real
|
||||
* read/write/delete logic runs against inspectable storage.
|
||||
*/
|
||||
private fun stubInMemoryBlobs() {
|
||||
val blobs = AppDependencies.blobs
|
||||
|
||||
every { blobs.forData(any<ByteArray>()) } answers {
|
||||
val bytes = firstArg<ByteArray>()
|
||||
mockk<BlobProvider.MemoryBlobBuilder> {
|
||||
every { createForMultipleSessionsOnDisk(any()) } answers {
|
||||
val uri = Uri.parse("memoryblob://registration/${blobCounter++}")
|
||||
blobData[uri] = bytes
|
||||
uri
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
every { blobs.getStream(any(), any()) } answers {
|
||||
val uri = secondArg<Uri>()
|
||||
blobData[uri]?.let { ByteArrayInputStream(it) } ?: throw IOException("No blob for $uri")
|
||||
}
|
||||
|
||||
every { blobs.delete(any(), any()) } answers {
|
||||
blobData.remove(secondArg<Uri>())
|
||||
}
|
||||
}
|
||||
|
||||
private fun seedInProgressData(data: RegistrationData) {
|
||||
val uri = Uri.parse("memoryblob://registration/seed-${blobCounter++}")
|
||||
blobData[uri] = RegistrationData.ADAPTER.encode(data)
|
||||
SignalStore.registration.inProgressRegistrationDataBlobUri = uri.toString()
|
||||
}
|
||||
|
||||
private fun readInProgressData(): RegistrationData = runBlocking { controller.readInProgressRegistrationData() }
|
||||
|
||||
private fun accountData(servicePassword: String = SERVICE_PASSWORD, linkedDeviceData: LinkedDeviceData? = null): AccountData {
|
||||
return AccountData(
|
||||
aciIdentityKeyPair = aciIdentity.serialize().toByteString(),
|
||||
pniIdentityKeyPair = pniIdentity.serialize().toByteString(),
|
||||
aciSignedPreKey = aciSignedPreKey.serialize().toByteString(),
|
||||
pniSignedPreKey = pniSignedPreKey.serialize().toByteString(),
|
||||
aciLastResortKyberPreKey = aciLastResortKyberPreKey.serialize().toByteString(),
|
||||
pniLastResortKyberPreKey = pniLastResortKyberPreKey.serialize().toByteString(),
|
||||
aciRegistrationId = 12345,
|
||||
pniRegistrationId = 54321,
|
||||
aci = aci.toString(),
|
||||
pni = pni.toString(),
|
||||
e164 = E164,
|
||||
servicePassword = servicePassword,
|
||||
linkedDeviceData = linkedDeviceData
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2026 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.thoughtcrime.securesms.testutil
|
||||
|
||||
import android.app.Application
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import org.junit.rules.ExternalResource
|
||||
import org.thoughtcrime.securesms.keyvalue.KeyValueDataSet
|
||||
import org.thoughtcrime.securesms.keyvalue.KeyValueStore
|
||||
import org.thoughtcrime.securesms.keyvalue.MockKeyValuePersistentStorage
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
|
||||
/**
|
||||
* Installs a real [SignalStore] backed by in-memory storage, letting tests arrange and assert on actual
|
||||
* stored state rather than stubbing interactions. Use [MockSignalStoreRule] instead when you need
|
||||
* interaction-based mocking.
|
||||
*/
|
||||
class SignalStoreRule : ExternalResource() {
|
||||
|
||||
override fun before() {
|
||||
val application = ApplicationProvider.getApplicationContext<Application>()
|
||||
SignalStore.testInject(SignalStore(application, KeyValueStore(MockKeyValuePersistentStorage.withDataSet(KeyValueDataSet()))))
|
||||
}
|
||||
|
||||
override fun after() {
|
||||
SignalStore.testInject(null)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user