diff --git a/app/build.gradle b/app/build.gradle index 96a41a18f..ba7359e23 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -168,6 +168,8 @@ android { } dependencies { + + implementation project(':wallet-sdk') implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:${libVersions.coroutines}" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:${libVersions.coroutines}" diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3cfb7f7ec..8226e8b8d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,21 @@ [versions] algosdk = "2.5.0" +coreKtx = "1.13.1" +junit = "4.13.2" +kotlinfixture = "1.2.0" +mockk = "1.13.13" +coroutines = "1.9.0" +kotlinTest = "1.5.31" [libraries] algosdk = { module = "com.algorand:algosdk", version.ref = "algosdk" } +kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } +core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +junit = { module = "junit:junit", version.ref = "junit" } +kotlinfixture = { module = "com.appmattus.fixture:fixture", version.ref = "kotlinfixture" } +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlinTest" } [bundles] diff --git a/settings.gradle.kts b/settings.gradle.kts index 33a11acb2..efab77488 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,2 +1,4 @@ -include(":app") rootProject.name = "algorand-android" +include(":app") +include(":wallet-sdk") +include(":test-utils") diff --git a/test-utils/.gitignore b/test-utils/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/test-utils/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/test-utils/build.gradle.kts b/test-utils/build.gradle.kts new file mode 100644 index 000000000..397a3ada0 --- /dev/null +++ b/test-utils/build.gradle.kts @@ -0,0 +1,14 @@ +plugins { + id("java-library") + id("org.jetbrains.kotlin.jvm") +} + +java { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 +} + +dependencies { + implementation(libs.kotlin.test) + implementation(libs.kotlinx.coroutines.test) +} diff --git a/test-utils/src/main/kotlin/com/algorand/test/TestObserver.kt b/test-utils/src/main/kotlin/com/algorand/test/TestObserver.kt new file mode 100644 index 000000000..562f76022 --- /dev/null +++ b/test-utils/src/main/kotlin/com/algorand/test/TestObserver.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.test + +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertTrue +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.test.UnconfinedTestDispatcher + +class TestObserver(private val flow: Flow, coroutineScope: CoroutineScope) { + private val emittedValues = mutableListOf() + private var flowError: Throwable? = null + private val job: Job = flow.onEach { + emittedValues.add(it) + }.catch { + flowError = it + }.launchIn(coroutineScope) + + fun assertError() { + assertNotNull(flowError) + } + + fun assertError(throwable: Throwable) { + assertEquals(flowError, throwable) + } + + fun assertNoValue() { + assertTrue(emittedValues.isEmpty()) + } + + fun assertValue(value: T) { + assertTrue(emittedValues.isNotEmpty()) + assertEquals(value, emittedValues.last()) + } + + fun assertValueHistory(vararg values: T) { + assertSize(values.size) + assertSequence(values.toList()) + } + + fun assertSize(size: Int) { + assertEquals(size, emittedValues.size) + } + + fun getValues() = emittedValues + + fun stopObserving() { + job.cancel() + } + + fun getFlow() = flow + + fun value(): T = getValues().last() + + private fun assertSequence(values: List) { + for ((index, v) in values.withIndex()) { + assertEquals(v, emittedValues[index]) + } + } +} + +@OptIn(ExperimentalCoroutinesApi::class) +fun Flow.test(coroutineScope: CoroutineScope = CoroutineScope(UnconfinedTestDispatcher())): TestObserver { + return TestObserver(this, coroutineScope) +} diff --git a/wallet-sdk/build.gradle.kts b/wallet-sdk/build.gradle.kts new file mode 100644 index 000000000..7b85b3c74 --- /dev/null +++ b/wallet-sdk/build.gradle.kts @@ -0,0 +1,68 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 + */ + +plugins { + id("org.jetbrains.kotlin.multiplatform") + id("com.android.library") +} + +apply(from = "../versions.gradle") + +val targets = extra["targets"] as Map<*, *> + +kotlin { + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = JavaVersion.VERSION_17.toString() + } + } + } + +// listOf( +// iosX64(), +// iosArm64(), +// iosSimulatorArm64() +// ).forEach { +// it.binaries.framework { +// baseName = "common" +// isStatic = true +// } +// } + + sourceSets { + androidMain.dependencies { + implementation(libs.kotlinfixture) + } + commonMain.dependencies { + implementation(libs.kotlinx.coroutines.core) + } + commonTest.dependencies { + implementation(project(":test-utils")) + implementation(libs.kotlin.test) + implementation(libs.kotlinx.coroutines.test) + implementation(libs.mockk) + } + } +} + +android { + namespace = "com.algorand.common" + compileSdk = targets["compileSdkVersion"] as Int + defaultConfig { + minSdk = targets["minSdkVersion"] as Int + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } +} diff --git a/wallet-sdk/src/androidMain/kotlin/com/algorand/common/testing/PeraFixture.kt b/wallet-sdk/src/androidMain/kotlin/com/algorand/common/testing/PeraFixture.kt new file mode 100644 index 000000000..4dfbb777a --- /dev/null +++ b/wallet-sdk/src/androidMain/kotlin/com/algorand/common/testing/PeraFixture.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.testing + +import com.appmattus.kotlinfixture.kotlinFixture + +class PeraFixture(private val listItemSize: Int) { + + val fixture = kotlinFixture { + repeatCount { listItemSize } + } + + inline operator fun invoke(): T { + return fixture() + } +} + +actual inline fun peraFixture(listItemSize: Int): T = PeraFixture(listItemSize).fixture() diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/model/LocalAccount.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/model/LocalAccount.kt new file mode 100644 index 000000000..68a6a0061 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/model/LocalAccount.kt @@ -0,0 +1,52 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.model + +sealed interface LocalAccount { + + val address: String + + data class Algo25( + override val address: String, + val secretKey: ByteArray + ) : LocalAccount { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as Algo25 + + if (address != other.address) return false + if (!secretKey.contentEquals(other.secretKey)) return false + + return true + } + + override fun hashCode(): Int { + var result = address.hashCode() + result = 31 * result + secretKey.contentHashCode() + return result + } + } + + data class LedgerBle( + override val address: String, + val deviceMacAddress: String, + val indexInLedger: Int + ) : LocalAccount + + data class NoAuth( + override val address: String + ) : LocalAccount +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/Algo25AccountRepository.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/Algo25AccountRepository.kt new file mode 100644 index 000000000..ca4608a22 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/Algo25AccountRepository.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.repository + +import com.algorand.common.account.local.model.LocalAccount +import kotlinx.coroutines.flow.Flow + +internal interface Algo25AccountRepository { + + fun getAllAsFlow(): Flow> + + fun getAccountCountAsFlow(): Flow + + suspend fun getAll(): List + + suspend fun getAccount(address: String): LocalAccount.Algo25? + + suspend fun addAccount(account: LocalAccount.Algo25) + + suspend fun deleteAccount(address: String) + + suspend fun deleteAllAccounts() +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/LedgerBleAccountRepository.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/LedgerBleAccountRepository.kt new file mode 100644 index 000000000..6b1b486b9 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/LedgerBleAccountRepository.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.repository + +import com.algorand.common.account.local.model.LocalAccount +import kotlinx.coroutines.flow.Flow + +internal interface LedgerBleAccountRepository { + + fun getAllAsFlow(): Flow> + + fun getAccountCountAsFlow(): Flow + + suspend fun getAll(): List + + suspend fun getAccount(address: String): LocalAccount.LedgerBle? + + suspend fun addAccount(account: LocalAccount.LedgerBle) + + suspend fun deleteAccount(address: String) + + suspend fun deleteAllAccounts() +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/NoAuthAccountRepository.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/NoAuthAccountRepository.kt new file mode 100644 index 000000000..a97e08e18 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/repository/NoAuthAccountRepository.kt @@ -0,0 +1,33 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.repository + +import com.algorand.common.account.local.model.LocalAccount +import kotlinx.coroutines.flow.Flow + +internal interface NoAuthAccountRepository { + + fun getAllAsFlow(): Flow> + + fun getAccountCountAsFlow(): Flow + + suspend fun getAll(): List + + suspend fun getAccount(address: String): LocalAccount.NoAuth? + + suspend fun addAccount(account: LocalAccount.NoAuth) + + suspend fun deleteAccount(address: String) + + suspend fun deleteAllAccounts() +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/DeleteLocalAccountUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/DeleteLocalAccountUseCase.kt new file mode 100644 index 000000000..8d89b26ce --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/DeleteLocalAccountUseCase.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository + +internal class DeleteLocalAccountUseCase( + private val algo25AccountRepository: Algo25AccountRepository, + private val noAuthAccountRepository: NoAuthAccountRepository, + private val ledgerBleAccountRepository: LedgerBleAccountRepository +) : DeleteLocalAccount { + + override suspend fun invoke(address: String) { + algo25AccountRepository.deleteAccount(address) + noAuthAccountRepository.deleteAccount(address) + ledgerBleAccountRepository.deleteAccount(address) + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetAllLocalAccountAddressesAsFlowUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetAllLocalAccountAddressesAsFlowUseCase.kt new file mode 100644 index 000000000..1d05fe309 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetAllLocalAccountAddressesAsFlowUseCase.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine + +internal class GetAllLocalAccountAddressesAsFlowUseCase( + private val algo25AccountRepository: Algo25AccountRepository, + private val ledgerBleAccountRepository: LedgerBleAccountRepository, + private val noAuthAccountRepository: NoAuthAccountRepository +) : GetAllLocalAccountAddressesAsFlow { + + override fun invoke(): Flow> { + return combine( + algo25AccountRepository.getAllAsFlow(), + ledgerBleAccountRepository.getAllAsFlow(), + noAuthAccountRepository.getAllAsFlow() + ) { algo25Accounts, ledgerBleAccounts, noAuthAccounts -> + buildList { + addAll(algo25Accounts.map { it.address }) + addAll(ledgerBleAccounts.map { it.address }) + addAll(noAuthAccounts.map { it.address }) + } + } + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountCountFlowUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountCountFlowUseCase.kt new file mode 100644 index 000000000..119776252 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountCountFlowUseCase.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.distinctUntilChanged + +internal class GetLocalAccountCountFlowUseCase( + private val algo25AccountRepository: Algo25AccountRepository, + private val ledgerBleAccountRepository: LedgerBleAccountRepository, + private val noAuthAccountRepository: NoAuthAccountRepository +) : GetLocalAccountCountFlow { + + override fun invoke(): Flow { + return combine( + algo25AccountRepository.getAccountCountAsFlow(), + ledgerBleAccountRepository.getAccountCountAsFlow(), + noAuthAccountRepository.getAccountCountAsFlow() + ) { algo25Count, ledgerBleCount, noAuthCount -> + algo25Count + ledgerBleCount + noAuthCount + }.distinctUntilChanged() + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountsUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountsUseCase.kt new file mode 100644 index 000000000..5a3de0171 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountsUseCase.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.async +import kotlinx.coroutines.awaitAll +import kotlinx.coroutines.withContext + +internal class GetLocalAccountsUseCase( + private val algo25AccountRepository: Algo25AccountRepository, + private val ledgerBleAccountRepository: LedgerBleAccountRepository, + private val noAuthAccountRepository: NoAuthAccountRepository, + private val dispatcher: CoroutineDispatcher +) : GetLocalAccounts { + + override suspend fun invoke(): List { + return withContext(dispatcher) { + val deferredAlgo25Accounts = async { algo25AccountRepository.getAll() } + val deferredLedgerBleAccounts = async { ledgerBleAccountRepository.getAll() } + val deferredNoAuthAccounts = async { noAuthAccountRepository.getAll() } + awaitAll( + deferredAlgo25Accounts, + deferredLedgerBleAccounts, + deferredNoAuthAccounts + ).flatten() + } + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetSecretKeyUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetSecretKeyUseCase.kt new file mode 100644 index 000000000..f39dbd55b --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/GetSecretKeyUseCase.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.repository.Algo25AccountRepository + +internal class GetSecretKeyUseCase( + private val algo25AccountRepository: Algo25AccountRepository +) : GetSecretKey { + + override suspend fun invoke(address: String): ByteArray? { + return algo25AccountRepository.getAccount(address)?.secretKey + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/IsThereAnyAccountWithAddressUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/IsThereAnyAccountWithAddressUseCase.kt new file mode 100644 index 000000000..21fdeda35 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/IsThereAnyAccountWithAddressUseCase.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +internal class IsThereAnyAccountWithAddressUseCase( + private val getLocalAccounts: GetLocalAccounts +) : IsThereAnyAccountWithAddress { + + override suspend fun invoke(address: String): Boolean { + return getLocalAccounts().any { it.address == address } + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/IsThereAnyLocalAccountUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/IsThereAnyLocalAccountUseCase.kt new file mode 100644 index 000000000..9af0b1db2 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/IsThereAnyLocalAccountUseCase.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +internal class IsThereAnyLocalAccountUseCase( + private val getLocalAccounts: GetLocalAccounts +) : IsThereAnyLocalAccount { + + override suspend fun invoke(): Boolean { + return getLocalAccounts().isNotEmpty() + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/LocalAccountUseCases.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/LocalAccountUseCases.kt new file mode 100644 index 000000000..24c2cd3f3 --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/LocalAccountUseCases.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount +import kotlinx.coroutines.flow.Flow + +fun interface AddAlgo25Account { + suspend operator fun invoke(account: LocalAccount.Algo25) +} + +fun interface AddLedgerBleAccount { + suspend operator fun invoke(account: LocalAccount.LedgerBle) +} + +fun interface AddNoAuthAccount { + suspend operator fun invoke(account: LocalAccount.NoAuth) +} + +fun interface CreateAlgo25Account { + suspend operator fun invoke(address: String, secretKey: ByteArray) +} + +fun interface CreateLedgerBleAccount { + suspend operator fun invoke(address: String, deviceMacAddress: String, indexInLedger: Int) +} + +fun interface CreateNoAuthAccount { + suspend operator fun invoke(address: String) +} + +fun interface DeleteLocalAccount { + suspend operator fun invoke(address: String) +} + +fun interface GetAllLocalAccountAddressesAsFlow { + operator fun invoke(): Flow> +} + +fun interface GetLedgerBleAccount { + suspend operator fun invoke(address: String): LocalAccount.LedgerBle? +} + +fun interface GetLocalAccountCountFlow { + operator fun invoke(): Flow +} + +fun interface GetLocalAccounts { + suspend operator fun invoke(): List +} + +fun interface GetSecretKey { + suspend operator fun invoke(address: String): ByteArray? +} + +fun interface IsThereAnyAccountWithAddress { + suspend operator fun invoke(address: String): Boolean +} + +fun interface IsThereAnyLocalAccount { + suspend operator fun invoke(): Boolean +} + +fun interface UpdateNoAuthAccountToAlgo25 { + suspend operator fun invoke(address: String, secretKey: ByteArray) +} + +fun interface UpdateNoAuthAccountToLedgerBle { + suspend operator fun invoke(address: String, deviceMacAddress: String, indexInLedger: Int) +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToAlgo25UseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToAlgo25UseCase.kt new file mode 100644 index 000000000..140d68ece --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToAlgo25UseCase.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +internal class UpdateNoAuthAccountToAlgo25UseCase( + private val deleteLocalAccount: DeleteLocalAccount, + private val createAlgo25Account: CreateAlgo25Account +) : UpdateNoAuthAccountToAlgo25 { + + override suspend fun invoke(address: String, secretKey: ByteArray) { + deleteLocalAccount(address) + createAlgo25Account(address, secretKey) + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToLedgerBleUseCase.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToLedgerBleUseCase.kt new file mode 100644 index 000000000..e8a5ccb1b --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToLedgerBleUseCase.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +internal class UpdateNoAuthAccountToLedgerBleUseCase( + private val deleteLocalAccount: DeleteLocalAccount, + private val createLedgerBleAccount: CreateLedgerBleAccount +) : UpdateNoAuthAccountToLedgerBle { + + override suspend fun invoke(address: String, deviceMacAddress: String, indexInLedger: Int) { + deleteLocalAccount(address) + createLedgerBleAccount(address, deviceMacAddress, indexInLedger) + } +} diff --git a/wallet-sdk/src/commonMain/kotlin/com/algorand/common/testing/PeraFixture.kt b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/testing/PeraFixture.kt new file mode 100644 index 000000000..cfc9533ba --- /dev/null +++ b/wallet-sdk/src/commonMain/kotlin/com/algorand/common/testing/PeraFixture.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.testing + +expect inline fun peraFixture(listItemSize: Int = 1): T diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/DeleteLocalAccountUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/DeleteLocalAccountUseCaseTest.kt new file mode 100644 index 000000000..27ba177e8 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/DeleteLocalAccountUseCaseTest.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Test + +internal class DeleteLocalAccountUseCaseTest { + + private val algo25AccountRepository: Algo25AccountRepository = mockk(relaxed = true) + private val noAuthAccountRepository: NoAuthAccountRepository = mockk(relaxed = true) + private val ledgerBleAccountRepository: LedgerBleAccountRepository = mockk(relaxed = true) + + private val deleteLocalAccount = DeleteLocalAccountUseCase( + algo25AccountRepository = algo25AccountRepository, + noAuthAccountRepository = noAuthAccountRepository, + ledgerBleAccountRepository = ledgerBleAccountRepository + ) + + @Test + fun `EXPECT all account repositories to delete account`() = runTest { + deleteLocalAccount("address") + + coVerify { algo25AccountRepository.deleteAccount("address") } + coVerify { ledgerBleAccountRepository.deleteAccount("address") } + coVerify { noAuthAccountRepository.deleteAccount("address") } + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetAllLocalAccountAddressesAsFlowUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetAllLocalAccountAddressesAsFlowUseCaseTest.kt new file mode 100644 index 000000000..a8e0be235 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetAllLocalAccountAddressesAsFlowUseCaseTest.kt @@ -0,0 +1,72 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount.Algo25 +import com.algorand.common.account.local.model.LocalAccount.LedgerBle +import com.algorand.common.account.local.model.LocalAccount.NoAuth +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import com.algorand.common.testing.peraFixture +import com.algorand.test.test +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.flowOf +import org.junit.Test + +class GetAllLocalAccountAddressesAsFlowUseCaseTest { + + private val algo25AccountRepository: Algo25AccountRepository = mockk() + private val ledgerBleAccountRepository: LedgerBleAccountRepository = mockk() + private val noAuthAccountRepository: NoAuthAccountRepository = mockk() + + private val sut = GetAllLocalAccountAddressesAsFlowUseCase( + algo25AccountRepository, + ledgerBleAccountRepository, + noAuthAccountRepository + ) + + @Test + fun `EXPECT empty list when all repositories return empty list`() { + every { algo25AccountRepository.getAllAsFlow() } returns flowOf(emptyList()) + every { ledgerBleAccountRepository.getAllAsFlow() } returns flowOf(emptyList()) + every { noAuthAccountRepository.getAllAsFlow() } returns flowOf(emptyList()) + + val testObserver = sut().test() + + testObserver.stopObserving() + testObserver.assertValue(emptyList()) + } + + @Test + fun `EXPECT account addresses WHEN there are local accounts`() { + every { algo25AccountRepository.getAllAsFlow() } returns flowOf(listOf(ALGO_25_ACCOUNT)) + every { ledgerBleAccountRepository.getAllAsFlow() } returns flowOf(listOf(LEDGER_BLE_ACCOUNT)) + every { noAuthAccountRepository.getAllAsFlow() } returns flowOf(listOf(NO_AUTH_ACCOUNT)) + + val testObserver = sut().test() + + testObserver.stopObserving() + testObserver.assertValue(listOf(ALGO_25_ADDRESS, LEDGER_BLE_ADDRESS, NO_AUTH_ADDRESS)) + } + + companion object { + private const val ALGO_25_ADDRESS = "address1" + private val ALGO_25_ACCOUNT = peraFixture().copy(address = ALGO_25_ADDRESS) + private const val LEDGER_BLE_ADDRESS = "address2" + private val LEDGER_BLE_ACCOUNT = peraFixture().copy(address = LEDGER_BLE_ADDRESS) + private const val NO_AUTH_ADDRESS = "address4" + private val NO_AUTH_ACCOUNT = peraFixture().copy(address = NO_AUTH_ADDRESS) + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountCountFlowUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountCountFlowUseCaseTest.kt new file mode 100644 index 000000000..c884d2f60 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountCountFlowUseCaseTest.kt @@ -0,0 +1,71 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import com.algorand.test.test +import io.mockk.every +import io.mockk.mockk +import kotlinx.coroutines.flow.flowOf +import org.junit.Test + +class GetLocalAccountCountFlowUseCaseTest { + + private val algo25AccountRepository: Algo25AccountRepository = mockk() + private val ledgerBleAccountRepository: LedgerBleAccountRepository = mockk() + private val noAuthAccountRepository: NoAuthAccountRepository = mockk() + + private val sut = GetLocalAccountCountFlowUseCase( + algo25AccountRepository, + ledgerBleAccountRepository, + noAuthAccountRepository + ) + + @Test + fun `EXPECT zero WHEN all repositories return zero`() { + every { algo25AccountRepository.getAccountCountAsFlow() } returns flowOf(0) + every { ledgerBleAccountRepository.getAccountCountAsFlow() } returns flowOf(0) + every { noAuthAccountRepository.getAccountCountAsFlow() } returns flowOf(0) + + val testObserver = sut().test() + + testObserver.stopObserving() + testObserver.assertValue(0) + } + + @Test + fun `EXPECT account count WHEN there are local accounts`() { + every { algo25AccountRepository.getAccountCountAsFlow() } returns flowOf(1) + every { ledgerBleAccountRepository.getAccountCountAsFlow() } returns flowOf(2) + every { noAuthAccountRepository.getAccountCountAsFlow() } returns flowOf(3) + + val testObserver = sut().test() + + testObserver.stopObserving() + testObserver.assertValue(6) + } + + @Test + fun `EXPECT count to be updated WHEN latest count is different than the current one`() { + every { algo25AccountRepository.getAccountCountAsFlow() } returns flowOf(1, 1) + every { ledgerBleAccountRepository.getAccountCountAsFlow() } returns flowOf(2, 2) + every { noAuthAccountRepository.getAccountCountAsFlow() } returns flowOf(3, 3) + + val testObserver = sut().test() + + testObserver.stopObserving() + testObserver.assertValueHistory(6) + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountsUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountsUseCaseTest.kt new file mode 100644 index 000000000..296963c6b --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetLocalAccountsUseCaseTest.kt @@ -0,0 +1,61 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.account.local.repository.LedgerBleAccountRepository +import com.algorand.common.account.local.repository.NoAuthAccountRepository +import com.algorand.common.testing.peraFixture +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.delay +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertEquals +import org.junit.Test + +class GetLocalAccountsUseCaseTest { + + private val algo25AccountRepository: Algo25AccountRepository = mockk() + private val ledgerBleAccountRepository: LedgerBleAccountRepository = mockk() + private val noAuthAccountRepository: NoAuthAccountRepository = mockk() + private val coroutineDispatcher = UnconfinedTestDispatcher() + + private val sut = GetLocalAccountsUseCase( + algo25AccountRepository, + ledgerBleAccountRepository, + noAuthAccountRepository, + coroutineDispatcher + ) + + @Test(timeout = 5000L) + fun `EXPECT local accounts and all calls to be made async`() = runTest(coroutineDispatcher) { + coEvery { algo25AccountRepository.getAll() } coAnswers { delay(10000L); ALGO_25_ACCOUNTS } + coEvery { ledgerBleAccountRepository.getAll() } coAnswers { delay(10000L); LEDGER_BLE_ACCOUNTS } + coEvery { noAuthAccountRepository.getAll() } coAnswers { delay(10000L); NO_AUTH_ACCOUNTS } + + val result = sut() + + coroutineDispatcher.scheduler.advanceTimeBy(10001L) + + val expected = ALGO_25_ACCOUNTS + LEDGER_BLE_ACCOUNTS + NO_AUTH_ACCOUNTS + assertEquals(expected, result) + } + + companion object { + private val ALGO_25_ACCOUNTS = peraFixture>() + private val LEDGER_BLE_ACCOUNTS = peraFixture>() + private val NO_AUTH_ACCOUNTS = peraFixture>() + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetSecretKeyUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetSecretKeyUseCaseTest.kt new file mode 100644 index 000000000..a6c6fcab2 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/GetSecretKeyUseCaseTest.kt @@ -0,0 +1,54 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount +import com.algorand.common.account.local.repository.Algo25AccountRepository +import com.algorand.common.testing.peraFixture +import io.mockk.coEvery +import io.mockk.mockk +import junit.framework.TestCase.assertTrue +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertNull +import org.junit.Test + +class GetSecretKeyUseCaseTest { + + private val algo25AccountRepository: Algo25AccountRepository = mockk() + + private val sut = GetSecretKeyUseCase(algo25AccountRepository) + + @Test + fun `EXPECT secret key WHEN account is found`() = runTest { + coEvery { algo25AccountRepository.getAccount(ALGO_25_ADDRESS) } returns ALGO_25_ACCOUNT + + val result = sut(ALGO_25_ADDRESS) + + assertTrue(result.contentEquals(ALGO_25_ACCOUNT.secretKey)) + } + + @Test + fun `EXPECT null WHEN account is not found`() = runTest { + coEvery { algo25AccountRepository.getAccount(ALGO_25_ADDRESS) } returns null + + val result = sut(ALGO_25_ADDRESS) + + assertNull(result) + } + + companion object { + private const val ALGO_25_ADDRESS = "ADDRESS_1" + + private val ALGO_25_ACCOUNT = peraFixture().copy(address = ALGO_25_ADDRESS) + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/IsThereAnyAccountWithAddressUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/IsThereAnyAccountWithAddressUseCaseTest.kt new file mode 100644 index 000000000..43fb2f7a9 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/IsThereAnyAccountWithAddressUseCaseTest.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount.Algo25 +import com.algorand.common.account.local.model.LocalAccount.LedgerBle +import com.algorand.common.account.local.model.LocalAccount.NoAuth +import com.algorand.common.testing.peraFixture +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class IsThereAnyAccountWithAddressUseCaseTest { + + private val getLocalAccounts: GetLocalAccounts = mockk() + + private val sut = IsThereAnyAccountWithAddressUseCase(getLocalAccounts) + + @Test + fun `EXPECT true WHEN account is found`() = runTest { + val localAccounts = listOf( + ALGO_25_ACCOUNT, + NO_AUTH_ACCOUNT, + LEDGER_BLE_ACCOUNT + ) + coEvery { getLocalAccounts() } returns localAccounts + + val result = sut(ALGO_25_ADDRESS) + + assertTrue(result) + } + + @Test + fun `EXPECT false WHEN account is not found`() = runTest { + val localAccounts = listOf( + NO_AUTH_ACCOUNT, + LEDGER_BLE_ACCOUNT + ) + coEvery { getLocalAccounts() } returns localAccounts + + val result = sut(ALGO_25_ADDRESS) + + assertFalse(result) + } + + @Test + fun `EXPECT false WHEN local account list is empty`() = runTest { + coEvery { getLocalAccounts() } returns emptyList() + + val result = sut(ALGO_25_ADDRESS) + + assertFalse(result) + } + + companion object { + private const val ALGO_25_ADDRESS = "ADDRESS_1" + private const val NO_AUTH_ADDRESS = "ADDRESS_2" + private const val LEDGER_BLE_ADDRESS = "ADDRESS_3" + + private val ALGO_25_ACCOUNT = peraFixture().copy(address = ALGO_25_ADDRESS) + private val NO_AUTH_ACCOUNT = peraFixture().copy(address = NO_AUTH_ADDRESS) + private val LEDGER_BLE_ACCOUNT = peraFixture().copy(address = LEDGER_BLE_ADDRESS) + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/IsThereAnyLocalAccountUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/IsThereAnyLocalAccountUseCaseTest.kt new file mode 100644 index 000000000..9eb98d481 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/IsThereAnyLocalAccountUseCaseTest.kt @@ -0,0 +1,51 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import com.algorand.common.account.local.model.LocalAccount +import com.algorand.common.testing.peraFixture +import io.mockk.coEvery +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class IsThereAnyLocalAccountUseCaseTest { + + private val getLocalAccounts: GetLocalAccounts = mockk() + + private val sut = IsThereAnyLocalAccountUseCase(getLocalAccounts) + + @Test + fun `EXPECT true WHEN there are local accounts`() = runTest { + coEvery { getLocalAccounts() } returns LOCAL_ACCOUNTS + + val result = sut() + + assertTrue(result) + } + + @Test + fun `EXPECT false WHEN there are not any local accounts`() = runTest { + coEvery { getLocalAccounts() } returns emptyList() + + val result = sut() + + assertFalse(result) + } + + companion object { + private val LOCAL_ACCOUNTS = peraFixture>() + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToAlgo25UseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToAlgo25UseCaseTest.kt new file mode 100644 index 000000000..02aea4ec7 --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToAlgo25UseCaseTest.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class UpdateNoAuthAccountToAlgo25UseCaseTest { + + private val deleteLocalAccount: DeleteLocalAccount = mockk(relaxed = true) + private val createAlgo25Account: CreateAlgo25Account = mockk(relaxed = true) + + private val sut = UpdateNoAuthAccountToAlgo25UseCase(deleteLocalAccount, createAlgo25Account) + + @Test + fun `EXPECT noAuthAccount to be deleted and new Algo25Account to be created`() = runTest { + sut(ADDRESS, SECRET_KEY) + + coVerify { deleteLocalAccount(ADDRESS) } + coVerify { createAlgo25Account(ADDRESS, SECRET_KEY) } + } + + companion object { + private const val ADDRESS = "ADDRESS" + private val SECRET_KEY = byteArrayOf() + } +} diff --git a/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToLedgerBleUseCaseTest.kt b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToLedgerBleUseCaseTest.kt new file mode 100644 index 000000000..946c8914f --- /dev/null +++ b/wallet-sdk/src/commonTest/kotlin/com/algorand/common/account/local/usecase/UpdateNoAuthAccountToLedgerBleUseCaseTest.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2022 Pera Wallet, LDA + * 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 com.algorand.common.account.local.usecase + +import io.mockk.coVerify +import io.mockk.mockk +import kotlinx.coroutines.test.runTest +import org.junit.Test + +class UpdateNoAuthAccountToLedgerBleUseCaseTest { + + private val deleteLocalAccount: DeleteLocalAccount = mockk(relaxed = true) + private val createLedgerBleAccount: CreateLedgerBleAccount = mockk(relaxed = true) + + private val sut = UpdateNoAuthAccountToLedgerBleUseCase(deleteLocalAccount, createLedgerBleAccount) + + @Test + fun `EXPECT noAuthAccount to be deleted and new LedgerBleAccount to be created`() = runTest { + sut(ADDRESS, DEVICE_MAC_ADDRESS, INDEX_IN_LEDGER) + + coVerify { deleteLocalAccount(ADDRESS) } + coVerify { createLedgerBleAccount(ADDRESS, DEVICE_MAC_ADDRESS, INDEX_IN_LEDGER) } + } + + companion object { + private const val ADDRESS = "ADDRESS" + private const val DEVICE_MAC_ADDRESS = "DEVICE_MAC_ADDRESS" + private const val INDEX_IN_LEDGER = 0 + } +}