fix: require real ABSDK success before authentication
This commit is contained in:
@@ -1,9 +1,11 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<application
|
<application
|
||||||
android:name=".ABoxManagerApplication"
|
android:name=".ABoxManagerApplication"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
|
android:usesCleartextTraffic="true"
|
||||||
android:theme="@style/Theme.ABoxManager">
|
android:theme="@style/Theme.ABoxManager">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|||||||
+126
@@ -0,0 +1,126 @@
|
|||||||
|
package com.flagship.abox.manager.ui.session
|
||||||
|
|
||||||
|
import com.flagship.abox.manager.domain.DomainError
|
||||||
|
import com.flagship.abox.manager.domain.LoginCredentials
|
||||||
|
import com.flagship.abox.manager.domain.Password
|
||||||
|
import com.flagship.abox.manager.domain.ProviderProfile
|
||||||
|
import com.flagship.abox.manager.domain.ProviderProfileId
|
||||||
|
import com.flagship.abox.manager.domain.ProviderType
|
||||||
|
import com.flagship.abox.manager.domain.Session
|
||||||
|
import com.flagship.abox.manager.domain.SessionCoordinator
|
||||||
|
import com.flagship.abox.manager.domain.SessionGateway
|
||||||
|
import com.flagship.abox.manager.domain.Username
|
||||||
|
import com.flagship.abox.manager.testing.MainDispatcherRule
|
||||||
|
import com.flagship.abox.manager.testing.fakes.FakeCredentialStore
|
||||||
|
import com.flagship.abox.manager.testing.fakes.FakeLocalSessionGateway
|
||||||
|
import com.flagship.abox.manager.testing.fakes.FakeProviderProfileStore
|
||||||
|
import com.flagship.abox.manager.testing.fakes.FakeSessionGateway
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Regression tests for real ABox login gating: the authenticated UI may appear only when the
|
||||||
|
* active provider profile owns a successful session, and a failed ABox login stays on Login.
|
||||||
|
*/
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
class AppSessionViewModelTest {
|
||||||
|
|
||||||
|
@get:Rule
|
||||||
|
val mainDispatcherRule = MainDispatcherRule()
|
||||||
|
|
||||||
|
private val profiles = FakeProviderProfileStore()
|
||||||
|
private val remote = FakeSessionGateway()
|
||||||
|
private val local = FakeLocalSessionGateway()
|
||||||
|
private val credentials = LoginCredentials(
|
||||||
|
username = Username("user"),
|
||||||
|
password = Password("password"),
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun newCoordinator(remoteGateway: SessionGateway = remote) = SessionCoordinator(
|
||||||
|
profileStore = profiles,
|
||||||
|
credentialStore = FakeCredentialStore(),
|
||||||
|
remoteGateway = remoteGateway,
|
||||||
|
localGateway = local,
|
||||||
|
)
|
||||||
|
|
||||||
|
private suspend fun saveAndActivateAboxProfile(id: ProviderProfileId) {
|
||||||
|
profiles.saveProfile(ProviderProfile(id, ProviderType.ABOX, id.value))
|
||||||
|
profiles.setActiveProfile(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun failedAboxLoginLeavesUiOnLogin() = runTest {
|
||||||
|
saveAndActivateAboxProfile(aboxProfileId)
|
||||||
|
remote.failLogin(aboxProfileId, DomainError.Authentication())
|
||||||
|
val coordinator = newCoordinator()
|
||||||
|
val viewModel = AppSessionViewModel(coordinator, profiles)
|
||||||
|
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||||
|
viewModel.uiState.collect {}
|
||||||
|
}
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
coordinator.login(
|
||||||
|
profileId = aboxProfileId,
|
||||||
|
credentials = credentials,
|
||||||
|
rememberUsername = false,
|
||||||
|
autoLogin = false,
|
||||||
|
)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertTrue(viewModel.uiState.value is AppSessionUiState.Login)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun sessionForDifferentProfileCannotAuthenticateActiveProfile() = runTest {
|
||||||
|
profiles.saveProfile(ProviderProfile(otherProfileId, ProviderType.ABOX, otherProfileId.value))
|
||||||
|
remote.login(otherProfileId, credentials)
|
||||||
|
saveAndActivateAboxProfile(aboxProfileId)
|
||||||
|
|
||||||
|
// The gateway exposes the other profile's session no matter which profile is observed.
|
||||||
|
val crossProfileGateway = object : SessionGateway by remote {
|
||||||
|
override fun observeSession(profileId: ProviderProfileId): Flow<Session?> =
|
||||||
|
remote.observeSession(otherProfileId)
|
||||||
|
}
|
||||||
|
val viewModel = AppSessionViewModel(newCoordinator(crossProfileGateway), profiles)
|
||||||
|
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||||
|
viewModel.uiState.collect {}
|
||||||
|
}
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertTrue(viewModel.uiState.value is AppSessionUiState.Login)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun successfulAboxLoginAuthenticatesActiveProfile() = runTest {
|
||||||
|
saveAndActivateAboxProfile(aboxProfileId)
|
||||||
|
val coordinator = newCoordinator()
|
||||||
|
val viewModel = AppSessionViewModel(coordinator, profiles)
|
||||||
|
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
|
||||||
|
viewModel.uiState.collect {}
|
||||||
|
}
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
coordinator.login(
|
||||||
|
profileId = aboxProfileId,
|
||||||
|
credentials = credentials,
|
||||||
|
rememberUsername = false,
|
||||||
|
autoLogin = false,
|
||||||
|
)
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
val state = viewModel.uiState.value
|
||||||
|
assertTrue(state is AppSessionUiState.Authenticated)
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
val aboxProfileId = ProviderProfileId("abox-default")
|
||||||
|
val otherProfileId = ProviderProfileId("abox-other")
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user