Skip to content

Commit

Permalink
add support for deleteing a user account
Browse files Browse the repository at this point in the history
  • Loading branch information
frett committed Nov 9, 2023
1 parent 877f521 commit e81d36b
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.core.app.ActivityOptionsCompat
import dagger.Lazy
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.CoroutineScope
Expand All @@ -23,17 +25,21 @@ import kotlinx.coroutines.launch
import org.ccci.gto.android.common.Ordered
import org.cru.godtools.account.provider.AccountProvider
import org.cru.godtools.account.provider.AuthenticationException
import org.cru.godtools.api.UserApi

@Singleton
@OptIn(ExperimentalCoroutinesApi::class)
class GodToolsAccountManager @VisibleForTesting internal constructor(
@get:VisibleForTesting
internal val providers: List<AccountProvider>,
private val userApi: Lazy<UserApi>,
coroutineScope: CoroutineScope = CoroutineScope(SupervisorJob()),
) {
@Inject
internal constructor(providers: Set<@JvmSuppressWildcards AccountProvider>) :
this(providers.sortedWith(Ordered.COMPARATOR))
internal constructor(
providers: Set<@JvmSuppressWildcards AccountProvider>,
userApi: Lazy<UserApi>,
) : this(providers.sortedWith(Ordered.COMPARATOR), userApi)

// region Active Provider
@VisibleForTesting
Expand Down Expand Up @@ -98,6 +104,14 @@ class GodToolsAccountManager @VisibleForTesting internal constructor(
// trigger a logout for any provider we happen to be logged into
providers.forEach { launch { it.logout() } }
}

suspend fun deleteAccount() = try {
userApi.get().deleteUser()
.also { if (it.isSuccessful) logout() }
.isSuccessful
} catch (_: IOException) {
false
}
// endregion Login/Logout

internal suspend fun authenticateWithMobileContentApi() = activeProvider?.authenticateWithMobileContentApi(false)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.cru.godtools.account

import app.cash.turbine.test
import io.mockk.Called
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.coVerifySequence
import io.mockk.every
import io.mockk.excludeRecords
import io.mockk.mockk
import java.io.IOException
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
Expand All @@ -16,7 +20,12 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import okhttp3.ResponseBody.Companion.toResponseBody
import org.ccci.gto.android.common.jsonapi.model.JsonApiObject
import org.cru.godtools.account.provider.AccountProvider
import org.cru.godtools.api.UserApi
import org.cru.godtools.model.User
import retrofit2.Response

@OptIn(ExperimentalCoroutinesApi::class)
class GodToolsAccountManagerTest {
Expand All @@ -27,22 +36,28 @@ class GodToolsAccountManagerTest {
every { order } returns 1
coEvery { isAuthenticated } answers { provider1Authenticated.value }
every { isAuthenticatedFlow() } returns provider1Authenticated

excludeRecords { isAuthenticatedFlow() }
}
private val provider2 = mockk<AccountProvider>(relaxed = true) {
every { order } returns 2
coEvery { isAuthenticated } answers { provider2Authenticated.value }
every { isAuthenticatedFlow() } returns provider2Authenticated

excludeRecords { isAuthenticatedFlow() }
}
private val testScope = TestScope()
private val userApi: UserApi = mockk()

private val manager = GodToolsAccountManager(
providers = listOf(provider1, provider2),
coroutineScope = testScope.backgroundScope
userApi = { userApi },
coroutineScope = testScope.backgroundScope,
)

@Test
fun verifyInjectedProvidersSorted() {
val manager = GodToolsAccountManager(setOf(provider2, provider1))
val manager = GodToolsAccountManager(providers = setOf(provider2, provider1), userApi = { userApi })
assertEquals(listOf(provider1, provider2), manager.providers)
}

Expand Down Expand Up @@ -115,4 +130,46 @@ class GodToolsAccountManagerTest {
provider2.logout()
}
}

// region deleteAccount()
@Test
fun `deleteAccount()`() = testScope.runTest {
coEvery { userApi.deleteUser() } returns Response.success<JsonApiObject<User>>(204, null)

assertTrue(manager.deleteAccount())
coVerifySequence {
userApi.deleteUser()
provider1.logout()
provider2.logout()
}
}

@Test
fun `deleteAccount() - not authenticated`() = testScope.runTest {
coEvery { userApi.deleteUser() } returns Response.error(401, "".toResponseBody())

assertFalse(manager.deleteAccount())
coVerifySequence {
userApi.deleteUser()

// deleting the user failed, so don't log the user out
provider1 wasNot Called
provider2 wasNot Called
}
}

@Test
fun `deleteAccount() - IOException`() = testScope.runTest {
coEvery { userApi.deleteUser() } throws IOException()

assertFalse(manager.deleteAccount())
coVerifySequence {
userApi.deleteUser()

// deleting the user failed, so don't log the user out
provider1 wasNot Called
provider2 wasNot Called
}
}
// endregion deleteAccount()
}
4 changes: 4 additions & 0 deletions library/api/src/main/kotlin/org/cru/godtools/api/UserApi.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import org.ccci.gto.android.common.jsonapi.retrofit2.model.JsonApiRetrofitObject
import org.cru.godtools.model.User
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.DELETE
import retrofit2.http.GET
import retrofit2.http.PATCH
import retrofit2.http.QueryMap
Expand All @@ -18,4 +19,7 @@ interface UserApi {

@PATCH(PATH_USER)
suspend fun updateUser(@Body user: JsonApiRetrofitObject<User>): Response<JsonApiObject<User>>

@DELETE(PATH_USER)
suspend fun deleteUser(): Response<JsonApiObject<User>>
}

0 comments on commit e81d36b

Please sign in to comment.