mirror of
https://github.com/ProtonMail/protoncore_android.git
synced 2026-05-15 09:50:41 +00:00
feat(account-recovery): Add recovery state observer.
This commit is contained in:
@@ -14,6 +14,10 @@ public final class me/proton/core/accountrecovery/domain/AccountRecoveryState :
|
||||
public static fun values ()[Lme/proton/core/accountrecovery/domain/AccountRecoveryState;
|
||||
}
|
||||
|
||||
public final class me/proton/core/accountrecovery/domain/AccountRecoveryStateKt {
|
||||
public static final fun toAccountRecoveryState (Lme/proton/core/domain/type/IntEnum;)Lme/proton/core/accountrecovery/domain/AccountRecoveryState;
|
||||
}
|
||||
|
||||
public abstract interface class me/proton/core/accountrecovery/domain/CancelNotifications {
|
||||
public abstract fun invoke (Lme/proton/core/domain/entity/UserId;)V
|
||||
}
|
||||
@@ -44,7 +48,8 @@ public final class me/proton/core/accountrecovery/domain/usecase/CancelRecovery
|
||||
}
|
||||
|
||||
public final class me/proton/core/accountrecovery/domain/usecase/ObserveAccountRecoveryState {
|
||||
public fun <init> (Lme/proton/core/user/domain/repository/UserRepository;)V
|
||||
public fun <init> (Lme/proton/core/user/domain/UserManager;)V
|
||||
public final fun invoke (Lme/proton/core/domain/entity/UserId;Z)Lkotlinx/coroutines/flow/Flow;
|
||||
public static synthetic fun invoke$default (Lme/proton/core/accountrecovery/domain/usecase/ObserveAccountRecoveryState;Lme/proton/core/domain/entity/UserId;ZILjava/lang/Object;)Lkotlinx/coroutines/flow/Flow;
|
||||
}
|
||||
|
||||
|
||||
@@ -26,13 +26,13 @@ plugins {
|
||||
protonKotlinLibrary
|
||||
}
|
||||
|
||||
publishOption.shouldBePublishedAsLib = true
|
||||
|
||||
protonCoverage {
|
||||
// change when stubs implemented
|
||||
minLineCoveragePercentage.set(35)
|
||||
minBranchCoveragePercentage.set(82)
|
||||
minLineCoveragePercentage.set(86)
|
||||
}
|
||||
|
||||
publishOption.shouldBePublishedAsLib = true
|
||||
|
||||
dependencies {
|
||||
api(
|
||||
project(Module.authDomain),
|
||||
@@ -43,10 +43,23 @@ dependencies {
|
||||
`javax-inject`
|
||||
)
|
||||
|
||||
implementation(
|
||||
project(Module.kotlinUtil)
|
||||
)
|
||||
|
||||
testImplementation(
|
||||
`coroutines-test`,
|
||||
junit,
|
||||
`kotlin-test`,
|
||||
mockk
|
||||
)
|
||||
|
||||
testImplementation(
|
||||
project(Module.kotlinTest),
|
||||
`coroutines-test`,
|
||||
junit,
|
||||
`kotlin-test`,
|
||||
mockk,
|
||||
turbine
|
||||
)
|
||||
}
|
||||
|
||||
+14
-1
@@ -18,10 +18,23 @@
|
||||
|
||||
package me.proton.core.accountrecovery.domain
|
||||
|
||||
import me.proton.core.domain.type.IntEnum
|
||||
import me.proton.core.user.domain.entity.UserRecovery
|
||||
|
||||
public enum class AccountRecoveryState {
|
||||
None,
|
||||
GracePeriod,
|
||||
ResetPassword,
|
||||
Cancelled,
|
||||
Expired
|
||||
}
|
||||
}
|
||||
|
||||
public fun IntEnum<UserRecovery.State>.toAccountRecoveryState(): AccountRecoveryState =
|
||||
when (this.enum) {
|
||||
UserRecovery.State.None -> AccountRecoveryState.None
|
||||
UserRecovery.State.Grace -> AccountRecoveryState.GracePeriod
|
||||
UserRecovery.State.Cancelled -> AccountRecoveryState.Cancelled
|
||||
UserRecovery.State.Insecure -> AccountRecoveryState.ResetPassword
|
||||
UserRecovery.State.Expired -> AccountRecoveryState.Expired
|
||||
null -> AccountRecoveryState.None
|
||||
}
|
||||
|
||||
+10
-6
@@ -21,15 +21,19 @@ package me.proton.core.accountrecovery.domain.usecase
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
import me.proton.core.accountrecovery.domain.AccountRecoveryState
|
||||
import me.proton.core.accountrecovery.domain.IsAccountRecoveryEnabled
|
||||
import me.proton.core.accountrecovery.domain.toAccountRecoveryState
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.user.domain.repository.UserRepository
|
||||
import me.proton.core.user.domain.UserManager
|
||||
import javax.inject.Inject
|
||||
|
||||
public class ObserveAccountRecoveryState @Inject constructor(
|
||||
private val userRepository: UserRepository
|
||||
private val userManager: UserManager
|
||||
) {
|
||||
public operator fun invoke(userId: UserId, refresh: Boolean): Flow<AccountRecoveryState> = userRepository.observeUser(userId).map {
|
||||
AccountRecoveryState.GracePeriod // todo: hardcoded change later
|
||||
}
|
||||
public operator fun invoke(userId: UserId, refresh: Boolean = true): Flow<AccountRecoveryState> =
|
||||
userManager.observeUser(sessionUserId = userId, refresh = refresh)
|
||||
.map { user ->
|
||||
if (user == null) AccountRecoveryState.None
|
||||
else
|
||||
user.recovery?.state?.toAccountRecoveryState() ?: AccountRecoveryState.None
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.accountrecovery.domain
|
||||
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.unmockkStatic
|
||||
import me.proton.core.domain.type.IntEnum
|
||||
import me.proton.core.test.kotlin.assertEquals
|
||||
import me.proton.core.user.domain.entity.UserRecovery
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class AccountRecoveryStateKtTest {
|
||||
|
||||
@Before
|
||||
fun beforeEveryTest() {
|
||||
mockkStatic("me.proton.core.accountrecovery.domain.AccountRecoveryStateKt")
|
||||
}
|
||||
|
||||
@After
|
||||
fun afterEveryTest() {
|
||||
unmockkStatic("me.proton.core.accountrecovery.domain.AccountRecoveryStateKt")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `mapping works correctly`() {
|
||||
var testState = IntEnum(0, UserRecovery.State.None)
|
||||
var result = testState.toAccountRecoveryState()
|
||||
assertEquals(AccountRecoveryState.None, result)
|
||||
|
||||
testState = IntEnum(1, UserRecovery.State.Grace)
|
||||
result = testState.toAccountRecoveryState()
|
||||
assertEquals(AccountRecoveryState.GracePeriod, result)
|
||||
|
||||
testState = IntEnum(2, UserRecovery.State.Cancelled)
|
||||
result = testState.toAccountRecoveryState()
|
||||
assertEquals(AccountRecoveryState.Cancelled, result)
|
||||
|
||||
testState = IntEnum(3, UserRecovery.State.Insecure)
|
||||
result = testState.toAccountRecoveryState()
|
||||
assertEquals(AccountRecoveryState.ResetPassword, result)
|
||||
|
||||
testState = IntEnum(4, UserRecovery.State.Expired)
|
||||
result = testState.toAccountRecoveryState()
|
||||
assertEquals(AccountRecoveryState.Expired, result)
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Proton Technologies AG
|
||||
* This file is part of Proton AG and ProtonCore.
|
||||
*
|
||||
* ProtonCore is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* ProtonCore is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with ProtonCore. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package me.proton.core.accountrecovery.domain.usecase
|
||||
|
||||
import app.cash.turbine.test
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import me.proton.core.accountrecovery.domain.AccountRecoveryState
|
||||
import me.proton.core.domain.entity.UserId
|
||||
import me.proton.core.domain.type.IntEnum
|
||||
import me.proton.core.network.domain.session.SessionId
|
||||
import me.proton.core.user.domain.UserManager
|
||||
import me.proton.core.user.domain.entity.User
|
||||
import me.proton.core.user.domain.entity.UserRecovery
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class ObserveAccountRecoveryStateTest {
|
||||
|
||||
private val testUserId = UserId("test-user-id")
|
||||
|
||||
private val testUser = User(
|
||||
userId = testUserId,
|
||||
email = null,
|
||||
name = "test-username",
|
||||
displayName = null,
|
||||
currency = "CHF",
|
||||
credit = 0,
|
||||
usedSpace = 0,
|
||||
maxSpace = 100,
|
||||
maxUpload = 100,
|
||||
role = null,
|
||||
private = true,
|
||||
services = 1,
|
||||
subscribed = 0,
|
||||
delinquent = null,
|
||||
recovery = UserRecovery(
|
||||
state = IntEnum(1, UserRecovery.State.Grace),
|
||||
startTime = 1L,
|
||||
endTime = 10L,
|
||||
sessionId = SessionId("test-session-id"),
|
||||
reason = UserRecovery.Reason.Authentication
|
||||
),
|
||||
keys = emptyList()
|
||||
)
|
||||
private val user = MutableStateFlow<User?>(null)
|
||||
private val observeUser = mockk<UserManager>(relaxed = true) {
|
||||
coEvery { this@mockk.observeUser(testUserId, any()) } returns user
|
||||
}
|
||||
|
||||
private lateinit var useCase: ObserveAccountRecoveryState
|
||||
|
||||
@Before
|
||||
fun beforeEveryTest() {
|
||||
useCase = ObserveAccountRecoveryState(observeUser)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `null user returns state none`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, true).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `null user returns state none default refresh`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non null user updated after null`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, true).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
|
||||
user.emit(testUser)
|
||||
assertEquals(AccountRecoveryState.GracePeriod, awaitItem())
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non null user update after null becomes null again`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, true).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
|
||||
user.emit(testUser)
|
||||
assertEquals(AccountRecoveryState.GracePeriod, awaitItem())
|
||||
|
||||
user.emit(null)
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non null user update after null becomes null again no refresh`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, false).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
|
||||
user.emit(testUser)
|
||||
assertEquals(AccountRecoveryState.GracePeriod, awaitItem())
|
||||
|
||||
user.emit(null)
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `user returns state none`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, false).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
|
||||
user.emit(testUser.copy(
|
||||
recovery = UserRecovery(
|
||||
state = IntEnum(1, null),
|
||||
startTime = 1L,
|
||||
endTime = 10L,
|
||||
sessionId = SessionId("test-session-id"),
|
||||
reason = UserRecovery.Reason.Authentication
|
||||
)
|
||||
))
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non null user update after null becomes null again ResetPassword state`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, false).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
|
||||
user.emit(testUser.copy(
|
||||
recovery = UserRecovery(
|
||||
state = IntEnum(1, UserRecovery.State.Insecure),
|
||||
startTime = 1L,
|
||||
endTime = 10L,
|
||||
sessionId = SessionId("test-session-id"),
|
||||
reason = UserRecovery.Reason.Authentication
|
||||
)
|
||||
))
|
||||
assertEquals(AccountRecoveryState.ResetPassword, awaitItem())
|
||||
|
||||
user.emit(null)
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non null user update after null becomes null again Cancelled state`() = runTest {
|
||||
// WHEN
|
||||
useCase.invoke(testUserId, false).test {
|
||||
// THEN
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
|
||||
user.emit(testUser.copy(
|
||||
recovery = UserRecovery(
|
||||
state = IntEnum(1, UserRecovery.State.Cancelled),
|
||||
startTime = 1L,
|
||||
endTime = 10L,
|
||||
sessionId = SessionId("test-session-id"),
|
||||
reason = UserRecovery.Reason.Authentication
|
||||
)
|
||||
))
|
||||
assertEquals(AccountRecoveryState.Cancelled, awaitItem())
|
||||
|
||||
user.emit(null)
|
||||
assertEquals(AccountRecoveryState.None, awaitItem())
|
||||
cancelAndConsumeRemainingEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user