Add tests for extracting and migrate data
This commit is contained in:
parent
f9f885418a
commit
88733784cd
|
@ -83,6 +83,11 @@ android {
|
|||
test {
|
||||
java.srcDirs += "src/sharedTest/java"
|
||||
}
|
||||
main {
|
||||
assets {
|
||||
srcDirs 'src/main/assets', 'src/androidTest/assets'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import io.realm.Realm
|
||||
import org.amshove.kluent.internal.assertFails
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.TemporaryRealmConfigurationFactory
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||
import org.matrix.android.sdk.internal.crypto.store.migration.fixtures.rustCryptoStoreMigrationConfiguration
|
||||
import org.matrix.olm.OlmManager
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExtractMigrationDataUseCaseTest : InstrumentedTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val realmConfigurationFactory = TemporaryRealmConfigurationFactory()
|
||||
|
||||
private val extractMigrationData = ExtractMigrationDataUseCase()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
// Ensure Olm is initialized
|
||||
OlmManager()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun given_a_valid_crypto_store_realm_file_then_extraction_should_be_successful() {
|
||||
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = true)
|
||||
val migrationData = Realm.getInstance(realmConfiguration).use {
|
||||
extractMigrationData(it)
|
||||
}
|
||||
assertNotNull(migrationData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun given_an_empty_crypto_store_realm_file_then_extraction_should_throw() {
|
||||
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = false)
|
||||
assertFails {
|
||||
Realm.getInstance(realmConfiguration).use {
|
||||
extractMigrationData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.amshove.kluent.internal.assertEquals
|
||||
import org.junit.Assert.assertNotNull
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.common.TemporaryRealmConfigurationFactory
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||
import org.matrix.android.sdk.internal.crypto.store.migration.fixtures.rustCryptoStoreMigrationConfiguration
|
||||
import org.matrix.olm.OlmManager
|
||||
import uniffi.olm.OlmMachine
|
||||
import java.util.concurrent.CountDownLatch
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class RustCryptoStoreMigrateUseCaseTest : InstrumentedTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val realmConfigurationFactory = TemporaryRealmConfigurationFactory()
|
||||
|
||||
private val extractMigrationData = ExtractMigrationDataUseCase()
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
// Ensure Olm is initialized
|
||||
OlmManager()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() = runBlocking {
|
||||
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = true)
|
||||
val cryptoStoreMigrate = RustCryptoStoreMigrateUseCase(realmConfiguration, realmConfigurationFactory.root, extractMigrationData)
|
||||
val latch = CountDownLatch(1)
|
||||
val progressListener = ProgressListener(latch)
|
||||
val result = cryptoStoreMigrate(progressListener)
|
||||
latch.await()
|
||||
assert(result.isSuccess)
|
||||
|
||||
val machine = OlmMachine("@ganfra146:matrix.org", "UTDQCHKKNS",realmConfigurationFactory.root.path, null)
|
||||
assertEquals("mW7LWO4zmhH8Ttuvmzn27vm/USXSKBPgmg7FKQITLiU", machine.identityKeys()["ed25519"])
|
||||
assertNotNull(machine.getBackupKeys())
|
||||
val crossSigningStatus = machine.crossSigningStatus()
|
||||
assertTrue(crossSigningStatus.hasMaster)
|
||||
assertTrue(crossSigningStatus.hasSelfSigning)
|
||||
assertTrue(crossSigningStatus.hasUserSigning)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun given_an_empty_crypto_store_realm_file_then_migration_should_fail() = runBlocking {
|
||||
val realmConfiguration = realmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore = false)
|
||||
val cryptoStoreMigrate = RustCryptoStoreMigrateUseCase(realmConfiguration, realmConfigurationFactory.root, extractMigrationData)
|
||||
val progressListener = ProgressListener()
|
||||
val result = cryptoStoreMigrate(progressListener)
|
||||
assert(result.isFailure)
|
||||
}
|
||||
|
||||
private class ProgressListener(val latch: CountDownLatch? = null) : uniffi.olm.ProgressListener {
|
||||
override fun onProgress(progress: Int, total: Int) {
|
||||
if (progress == total) {
|
||||
latch?.countDown()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.store.migration.fixtures
|
||||
|
||||
import io.realm.RealmConfiguration
|
||||
import org.matrix.android.sdk.common.TemporaryRealmConfigurationFactory
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
||||
|
||||
fun TemporaryRealmConfigurationFactory.rustCryptoStoreMigrationConfiguration(populateCryptoStore: Boolean): RealmConfiguration {
|
||||
return create(
|
||||
realmFilename = "crypto_store_rust_migration.realm",
|
||||
assetFilename = "crypto_store_rust_migration.realm".takeIf { populateCryptoStore },
|
||||
schemaVersion = 15L,
|
||||
module = RealmCryptoStoreModule()
|
||||
)
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||
|
||||
class CleanUpCryptoStoreUseCase {
|
||||
}
|
|
@ -35,7 +35,15 @@ private val charset = Charset.forName("UTF-8")
|
|||
|
||||
internal class ExtractMigrationDataUseCase @Inject constructor() {
|
||||
|
||||
operator fun invoke(realm: Realm): MigrationData? {
|
||||
operator fun invoke(realm: Realm): MigrationData {
|
||||
return try {
|
||||
extract(realm) ?: throw ExtractMigrationDataFailure
|
||||
} catch (failure: Throwable) {
|
||||
throw ExtractMigrationDataFailure
|
||||
}
|
||||
}
|
||||
|
||||
private fun extract(realm: Realm): MigrationData? {
|
||||
val metadataEntity = realm.where<CryptoMetadataEntity>().findFirst() ?: return null
|
||||
|
||||
val pickleKey = OlmUtility.getRandomKey()
|
||||
|
@ -61,7 +69,7 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
|||
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
|
||||
|
||||
val olmAccount = metadataEntity.getOlmAccount()!!
|
||||
val pickledOlmAccount = olmAccount.pickle(pickleKey)
|
||||
val pickledOlmAccount = olmAccount.pickle(pickleKey, StringBuffer()).asString()
|
||||
val pickledAccount = PickledAccount(
|
||||
userId = userId,
|
||||
deviceId = deviceId,
|
||||
|
@ -88,7 +96,7 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
|||
private fun OlmInboundGroupSessionEntity.toPickledInboundGroupSession(pickleKey: ByteArray): PickledInboundGroupSession {
|
||||
val senderKey = this.senderKey ?: ""
|
||||
val olmInboundGroupSession = getInboundGroupSession()!!
|
||||
val pickledInboundGroupSession = olmInboundGroupSession.olmInboundGroupSession!!.pickle(pickleKey)
|
||||
val pickledInboundGroupSession = olmInboundGroupSession.olmInboundGroupSession!!.pickle(pickleKey, StringBuffer()).asString()
|
||||
return PickledInboundGroupSession(
|
||||
pickle = pickledInboundGroupSession,
|
||||
senderKey = senderKey,
|
||||
|
@ -104,7 +112,7 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
|||
val deviceKey = this.deviceKey ?: ""
|
||||
val lastReceivedMessageTs = this.lastReceivedMessageTs
|
||||
val olmSession = getOlmSession()!!
|
||||
val pickledOlmSession = olmSession.pickle(pickleKey)
|
||||
val pickledOlmSession = olmSession.pickle(pickleKey, StringBuffer()).asString()
|
||||
return PickledSession(
|
||||
pickle = pickledOlmSession,
|
||||
senderKey = deviceKey,
|
||||
|
@ -114,14 +122,5 @@ internal class ExtractMigrationDataUseCase @Inject constructor() {
|
|||
)
|
||||
}
|
||||
|
||||
private fun Any.pickle(pickleKey: ByteArray): String {
|
||||
return try {
|
||||
val pickleMethod = this.javaClass.getDeclaredMethod("serialize", ByteArray::class.java, StringBuffer::class.java)
|
||||
pickleMethod.isAccessible = true
|
||||
val pickled = pickleMethod.invoke(this, pickleKey, StringBuffer())!!
|
||||
String(pickled as ByteArray, charset)
|
||||
} catch (throwable: Throwable) {
|
||||
""
|
||||
}
|
||||
}
|
||||
private fun ByteArray.asString() = String(this, charset)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
* Copyright (c) 2022 New Vector Ltd
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.matrix.android.sdk.internal.crypto.store.migration
|
||||
|
||||
object ExtractMigrationDataFailure : java.lang.RuntimeException("Can't proceed with migration, crypto store is empty or some necessary data is missing.")
|
|
@ -21,7 +21,6 @@ import io.realm.RealmConfiguration
|
|||
import org.matrix.android.sdk.internal.database.awaitTransaction
|
||||
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||
import org.matrix.android.sdk.internal.di.SessionFilesDirectory
|
||||
import timber.log.Timber
|
||||
import uniffi.olm.ProgressListener
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
@ -38,10 +37,6 @@ internal class RustCryptoStoreMigrateUseCase @Inject constructor(
|
|||
private suspend fun migrate(progressListener: ProgressListener) {
|
||||
awaitTransaction(realmConfiguration) { realm: Realm ->
|
||||
val migrationData = extractMigrationData(realm)
|
||||
if (migrationData == null) {
|
||||
Timber.v("No migration to do, return")
|
||||
return@awaitTransaction
|
||||
}
|
||||
uniffi.olm.migrate(migrationData, dataDir.path, null, progressListener)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue