Loading app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt +136 −44 Original line number Diff line number Diff line Loading @@ -8,14 +8,21 @@ import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.login.core.AuthObject import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.domain.model.LoginState import foundation.e.apps.domain.model.PlayStoreAuthSource import foundation.e.apps.domain.model.User import foundation.e.apps.domain.preferences.AppPreferencesRepository import foundation.e.apps.domain.preferences.SessionRepository import io.mockk.clearAllMocks import io.mockk.coEvery import io.mockk.every import io.mockk.coVerify import io.mockk.mockk import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner Loading @@ -24,31 +31,34 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [30]) class PlayStoreAuthenticatorTest { private val json = Json { ignoreUnknownKeys = true } private fun applicationContext(): Context = ApplicationProvider.getApplicationContext() @After fun tearDown() { clearAllMocks() } @Test fun login_returnsSavedAuthWhenAvailable() = runTest { val context = applicationContext() val sessionRepository = mockk<SessionRepository>() val appPreferencesRepository = mockk<AppPreferencesRepository>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val googleLoginManager = mockk<GoogleLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>(relaxed = true) val aasTokenConverter = mockk<OauthToAasTokenConverter>(relaxed = true) val userProfileFetcher = mockk<UserProfileFetcher>(relaxed = true) val savedAuth = AuthData(email = "saved@example.com") every { sessionRepository.getUser() } returns User.ANONYMOUS every { authDataCache.getSavedAuthData() } returns savedAuth every { authDataCache.formatAuthData(savedAuth) } returns savedAuth val sessionRepository = FakeSessionRepository( initialUser = User.ANONYMOUS, authDataJson = json.encodeToString(savedAuth) ) val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>() val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, appPreferencesRepository = appPreferencesRepository, authDataCache = authDataCache, appPreferencesRepository = FakeAppPreferencesRepository(), authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, Loading @@ -61,33 +71,29 @@ class PlayStoreAuthenticatorTest { assertThat(result.authObject).isInstanceOf(AuthObject.GPlayAuth::class.java) val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result).isInstanceOf(ResultSupreme.Success::class.java) assertThat(authObject.result.data).isEqualTo(savedAuth) assertThat(result.authDataToPersist).isEqualTo(savedAuth) assertThat(authObject.result.data?.email).isEqualTo(savedAuth.email) assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0]) assertThat(result.authDataToPersist?.email).isEqualTo(savedAuth.email) } @Test fun login_usesAnonymousLoginWhenNoSavedAuth() = runTest { val context = applicationContext() val sessionRepository = mockk<SessionRepository>() val appPreferencesRepository = mockk<AppPreferencesRepository>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val googleLoginManager = mockk<GoogleLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val sessionRepository = FakeSessionRepository(initialUser = User.ANONYMOUS) val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>() val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>(relaxed = true) val userProfileFetcher = mockk<UserProfileFetcher>(relaxed = true) val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authData = AuthData(email = "anon@example.com", isAnonymous = true) every { sessionRepository.getUser() } returns User.ANONYMOUS every { authDataCache.getSavedAuthData() } returns null coEvery { anonymousLoginManager.login() } returns authData every { authDataCache.formatAuthData(any()) } answers { firstArg<AuthData>() } val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, appPreferencesRepository = appPreferencesRepository, authDataCache = authDataCache, appPreferencesRepository = FakeAppPreferencesRepository(), authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, Loading @@ -100,37 +106,37 @@ class PlayStoreAuthenticatorTest { assertThat(result.authObject).isInstanceOf(AuthObject.GPlayAuth::class.java) val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result).isInstanceOf(ResultSupreme.Success::class.java) assertThat(authObject.result.data).isEqualTo(authData) assertThat(result.authDataToPersist).isEqualTo(authData) assertThat(authObject.result.data?.email).isEqualTo(authData.email) assertThat(authObject.result.data?.isAnonymous).isTrue() assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0]) assertThat(result.authDataToPersist?.email).isEqualTo(authData.email) coVerify(exactly = 1) { anonymousLoginManager.login() } } @Test fun login_fetchesUserProfileForGoogle() = runTest { val context = applicationContext() val sessionRepository = mockk<SessionRepository>() val appPreferencesRepository = mockk<AppPreferencesRepository>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val sessionRepository = FakeSessionRepository( initialUser = User.GOOGLE, playStoreAuthSource = PlayStoreAuthSource.GOOGLE.name, aasToken = "aas-token" ) val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>() val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authData = AuthData(email = "user@gmail.com") val profile = mockk<UserProfile>() every { sessionRepository.getUser() } returns User.GOOGLE every { sessionRepository.playStoreAuthSource } returns flowOf("GOOGLE") every { sessionRepository.aasToken } returns flowOf("aas-token") every { authDataCache.getSavedAuthData() } returns null val profile = UserProfile(name = "Test User", email = authData.email) coEvery { googleLoginManager.login() } returns authData every { authDataCache.formatAuthData(any()) } answers { firstArg<AuthData>() } coEvery { userProfileFetcher.fetch(authData) } returns profile val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, appPreferencesRepository = appPreferencesRepository, authDataCache = authDataCache, appPreferencesRepository = FakeAppPreferencesRepository(), authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, Loading @@ -141,6 +147,92 @@ class PlayStoreAuthenticatorTest { val result = authenticator.login() val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result.data?.email).isEqualTo(authData.email) assertThat(authObject.result.data?.userProfile).isEqualTo(profile) assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0]) coVerify(exactly = 1) { googleLoginManager.login() } coVerify(exactly = 1) { userProfileFetcher.fetch(authData) } } private class FakeSessionRepository( initialUser: User, authDataJson: String = "", email: String = "", oauthToken: String = "", aasToken: String = "", playStoreAuthSource: String = "" ) : SessionRepository { override val authData: MutableStateFlow<String> = MutableStateFlow(authDataJson) override val emailData: MutableStateFlow<String> = MutableStateFlow(email) override val oauthToken: MutableStateFlow<String> = MutableStateFlow(oauthToken) override val aasToken: MutableStateFlow<String> = MutableStateFlow(aasToken) override val userType: MutableStateFlow<String> = MutableStateFlow(initialUser.name) override val playStoreAuthSource: MutableStateFlow<String> = MutableStateFlow(playStoreAuthSource) override val user: MutableStateFlow<User> = MutableStateFlow(initialUser) override val loginState: MutableStateFlow<LoginState> = MutableStateFlow(LoginState.AVAILABLE) override val tocStatus: Flow<Boolean> = flowOf(false) override val tosVersion: Flow<String> = flowOf("") override suspend fun saveAuthData(authData: AuthData?) { throw NotImplementedError("Not needed in this test") } override fun getAuthData(): AuthData { throw NotImplementedError("Not needed in this test") } override suspend fun destroyCredentials() { throw NotImplementedError("Not needed in this test") } override fun getUser(): User = user.value override fun getLoginState(): LoginState = loginState.value override fun isAnonymousUser(): Boolean = user.value == User.ANONYMOUS override suspend fun awaitUser(): User = user.value override suspend fun awaitLoginState(): LoginState = loginState.value override suspend fun awaitTosVersion(): String = "" override suspend fun saveUserType(user: User?) { this.user.value = user ?: User.NO_GOOGLE this.userType.value = this.user.value.name } override suspend fun saveAasToken(aasToken: String) { this.aasToken.value = aasToken } override suspend fun saveGoogleLogin(email: String, token: String) { this.emailData.value = email this.oauthToken.value = token saveUserType(User.GOOGLE) } override suspend fun savePlayStoreAuthSource(authSource: PlayStoreAuthSource?) { playStoreAuthSource.value = authSource?.name.orEmpty() } override suspend fun saveTocStatus(status: Boolean, tosVersion: String) = Unit } private class FakeAppPreferencesRepository : AppPreferencesRepository { override fun preferredApplicationType(): String = "" override fun isOpenSourceSelected(): Boolean = false override fun isPWASelected(): Boolean = false override fun isPlayStoreSelected(): Boolean = true override fun shouldShowUpdateNotification(): Boolean = false override fun isAutomaticInstallEnabled(): Boolean = false override fun disablePlayStore() = Unit override fun disableOpenSource() = Unit override fun disablePwa() = Unit override fun enablePlayStore() = Unit override fun enableOpenSource() = Unit override fun enablePwa() = Unit override fun shouldUpdateAppsFromOtherStores(): Boolean = false override fun isOnlyUnmeteredNetworkEnabled(): Boolean = false } } Loading
app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt +136 −44 Original line number Diff line number Diff line Loading @@ -8,14 +8,21 @@ import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.login.core.AuthObject import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.domain.model.LoginState import foundation.e.apps.domain.model.PlayStoreAuthSource import foundation.e.apps.domain.model.User import foundation.e.apps.domain.preferences.AppPreferencesRepository import foundation.e.apps.domain.preferences.SessionRepository import io.mockk.clearAllMocks import io.mockk.coEvery import io.mockk.every import io.mockk.coVerify import io.mockk.mockk import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import kotlinx.serialization.json.Json import org.junit.After import org.junit.Test import org.junit.runner.RunWith import org.robolectric.RobolectricTestRunner Loading @@ -24,31 +31,34 @@ import org.robolectric.annotation.Config @RunWith(RobolectricTestRunner::class) @Config(sdk = [30]) class PlayStoreAuthenticatorTest { private val json = Json { ignoreUnknownKeys = true } private fun applicationContext(): Context = ApplicationProvider.getApplicationContext() @After fun tearDown() { clearAllMocks() } @Test fun login_returnsSavedAuthWhenAvailable() = runTest { val context = applicationContext() val sessionRepository = mockk<SessionRepository>() val appPreferencesRepository = mockk<AppPreferencesRepository>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val googleLoginManager = mockk<GoogleLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>(relaxed = true) val aasTokenConverter = mockk<OauthToAasTokenConverter>(relaxed = true) val userProfileFetcher = mockk<UserProfileFetcher>(relaxed = true) val savedAuth = AuthData(email = "saved@example.com") every { sessionRepository.getUser() } returns User.ANONYMOUS every { authDataCache.getSavedAuthData() } returns savedAuth every { authDataCache.formatAuthData(savedAuth) } returns savedAuth val sessionRepository = FakeSessionRepository( initialUser = User.ANONYMOUS, authDataJson = json.encodeToString(savedAuth) ) val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>() val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, appPreferencesRepository = appPreferencesRepository, authDataCache = authDataCache, appPreferencesRepository = FakeAppPreferencesRepository(), authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, Loading @@ -61,33 +71,29 @@ class PlayStoreAuthenticatorTest { assertThat(result.authObject).isInstanceOf(AuthObject.GPlayAuth::class.java) val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result).isInstanceOf(ResultSupreme.Success::class.java) assertThat(authObject.result.data).isEqualTo(savedAuth) assertThat(result.authDataToPersist).isEqualTo(savedAuth) assertThat(authObject.result.data?.email).isEqualTo(savedAuth.email) assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0]) assertThat(result.authDataToPersist?.email).isEqualTo(savedAuth.email) } @Test fun login_usesAnonymousLoginWhenNoSavedAuth() = runTest { val context = applicationContext() val sessionRepository = mockk<SessionRepository>() val appPreferencesRepository = mockk<AppPreferencesRepository>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val googleLoginManager = mockk<GoogleLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val sessionRepository = FakeSessionRepository(initialUser = User.ANONYMOUS) val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>() val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>(relaxed = true) val userProfileFetcher = mockk<UserProfileFetcher>(relaxed = true) val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authData = AuthData(email = "anon@example.com", isAnonymous = true) every { sessionRepository.getUser() } returns User.ANONYMOUS every { authDataCache.getSavedAuthData() } returns null coEvery { anonymousLoginManager.login() } returns authData every { authDataCache.formatAuthData(any()) } answers { firstArg<AuthData>() } val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, appPreferencesRepository = appPreferencesRepository, authDataCache = authDataCache, appPreferencesRepository = FakeAppPreferencesRepository(), authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, Loading @@ -100,37 +106,37 @@ class PlayStoreAuthenticatorTest { assertThat(result.authObject).isInstanceOf(AuthObject.GPlayAuth::class.java) val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result).isInstanceOf(ResultSupreme.Success::class.java) assertThat(authObject.result.data).isEqualTo(authData) assertThat(result.authDataToPersist).isEqualTo(authData) assertThat(authObject.result.data?.email).isEqualTo(authData.email) assertThat(authObject.result.data?.isAnonymous).isTrue() assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0]) assertThat(result.authDataToPersist?.email).isEqualTo(authData.email) coVerify(exactly = 1) { anonymousLoginManager.login() } } @Test fun login_fetchesUserProfileForGoogle() = runTest { val context = applicationContext() val sessionRepository = mockk<SessionRepository>() val appPreferencesRepository = mockk<AppPreferencesRepository>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val sessionRepository = FakeSessionRepository( initialUser = User.GOOGLE, playStoreAuthSource = PlayStoreAuthSource.GOOGLE.name, aasToken = "aas-token" ) val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>() val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authData = AuthData(email = "user@gmail.com") val profile = mockk<UserProfile>() every { sessionRepository.getUser() } returns User.GOOGLE every { sessionRepository.playStoreAuthSource } returns flowOf("GOOGLE") every { sessionRepository.aasToken } returns flowOf("aas-token") every { authDataCache.getSavedAuthData() } returns null val profile = UserProfile(name = "Test User", email = authData.email) coEvery { googleLoginManager.login() } returns authData every { authDataCache.formatAuthData(any()) } answers { firstArg<AuthData>() } coEvery { userProfileFetcher.fetch(authData) } returns profile val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, appPreferencesRepository = appPreferencesRepository, authDataCache = authDataCache, appPreferencesRepository = FakeAppPreferencesRepository(), authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, Loading @@ -141,6 +147,92 @@ class PlayStoreAuthenticatorTest { val result = authenticator.login() val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result.data?.email).isEqualTo(authData.email) assertThat(authObject.result.data?.userProfile).isEqualTo(profile) assertThat(authObject.result.data?.locale).isEqualTo(context.resources.configuration.locales[0]) coVerify(exactly = 1) { googleLoginManager.login() } coVerify(exactly = 1) { userProfileFetcher.fetch(authData) } } private class FakeSessionRepository( initialUser: User, authDataJson: String = "", email: String = "", oauthToken: String = "", aasToken: String = "", playStoreAuthSource: String = "" ) : SessionRepository { override val authData: MutableStateFlow<String> = MutableStateFlow(authDataJson) override val emailData: MutableStateFlow<String> = MutableStateFlow(email) override val oauthToken: MutableStateFlow<String> = MutableStateFlow(oauthToken) override val aasToken: MutableStateFlow<String> = MutableStateFlow(aasToken) override val userType: MutableStateFlow<String> = MutableStateFlow(initialUser.name) override val playStoreAuthSource: MutableStateFlow<String> = MutableStateFlow(playStoreAuthSource) override val user: MutableStateFlow<User> = MutableStateFlow(initialUser) override val loginState: MutableStateFlow<LoginState> = MutableStateFlow(LoginState.AVAILABLE) override val tocStatus: Flow<Boolean> = flowOf(false) override val tosVersion: Flow<String> = flowOf("") override suspend fun saveAuthData(authData: AuthData?) { throw NotImplementedError("Not needed in this test") } override fun getAuthData(): AuthData { throw NotImplementedError("Not needed in this test") } override suspend fun destroyCredentials() { throw NotImplementedError("Not needed in this test") } override fun getUser(): User = user.value override fun getLoginState(): LoginState = loginState.value override fun isAnonymousUser(): Boolean = user.value == User.ANONYMOUS override suspend fun awaitUser(): User = user.value override suspend fun awaitLoginState(): LoginState = loginState.value override suspend fun awaitTosVersion(): String = "" override suspend fun saveUserType(user: User?) { this.user.value = user ?: User.NO_GOOGLE this.userType.value = this.user.value.name } override suspend fun saveAasToken(aasToken: String) { this.aasToken.value = aasToken } override suspend fun saveGoogleLogin(email: String, token: String) { this.emailData.value = email this.oauthToken.value = token saveUserType(User.GOOGLE) } override suspend fun savePlayStoreAuthSource(authSource: PlayStoreAuthSource?) { playStoreAuthSource.value = authSource?.name.orEmpty() } override suspend fun saveTocStatus(status: Boolean, tosVersion: String) = Unit } private class FakeAppPreferencesRepository : AppPreferencesRepository { override fun preferredApplicationType(): String = "" override fun isOpenSourceSelected(): Boolean = false override fun isPWASelected(): Boolean = false override fun isPlayStoreSelected(): Boolean = true override fun shouldShowUpdateNotification(): Boolean = false override fun isAutomaticInstallEnabled(): Boolean = false override fun disablePlayStore() = Unit override fun disableOpenSource() = Unit override fun disablePwa() = Unit override fun enablePlayStore() = Unit override fun enableOpenSource() = Unit override fun enablePwa() = Unit override fun shouldUpdateAppsFromOtherStores(): Boolean = false override fun isOnlyUnmeteredNetworkEnabled(): Boolean = false } }