diff --git a/app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt b/app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt index 32fd18a65fb8ab164c141f5035e54318c33154fe..c71c14d95283efad05acf81638a4fa8edce8c12c 100644 --- a/app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt +++ b/app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt @@ -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 @@ -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() - val appPreferencesRepository = mockk(relaxed = true) - val authDataCache = mockk() - val googleLoginManager = mockk(relaxed = true) - val microgLoginManager = mockk(relaxed = true) - val anonymousLoginManager = mockk(relaxed = true) - val aasTokenConverter = mockk(relaxed = true) - val userProfileFetcher = mockk(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() + val microgLoginManager = mockk() + val anonymousLoginManager = mockk() + val aasTokenConverter = mockk() + val userProfileFetcher = mockk() val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, - appPreferencesRepository = appPreferencesRepository, - authDataCache = authDataCache, + appPreferencesRepository = FakeAppPreferencesRepository(), + authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, @@ -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() - val appPreferencesRepository = mockk(relaxed = true) - val authDataCache = mockk() - val googleLoginManager = mockk(relaxed = true) - val microgLoginManager = mockk(relaxed = true) + val sessionRepository = FakeSessionRepository(initialUser = User.ANONYMOUS) + val googleLoginManager = mockk() + val microgLoginManager = mockk() val anonymousLoginManager = mockk() - val aasTokenConverter = mockk(relaxed = true) - val userProfileFetcher = mockk(relaxed = true) + val aasTokenConverter = mockk() + val userProfileFetcher = mockk() 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() } val authenticator = PlayStoreAuthenticator( context = context, sessionRepository = sessionRepository, - appPreferencesRepository = appPreferencesRepository, - authDataCache = authDataCache, + appPreferencesRepository = FakeAppPreferencesRepository(), + authDataCache = AuthDataCache(sessionRepository, json), googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, @@ -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() - val appPreferencesRepository = mockk(relaxed = true) - val authDataCache = mockk() + val sessionRepository = FakeSessionRepository( + initialUser = User.GOOGLE, + playStoreAuthSource = PlayStoreAuthSource.GOOGLE.name, + aasToken = "aas-token" + ) val googleLoginManager = mockk() - val microgLoginManager = mockk(relaxed = true) - val anonymousLoginManager = mockk(relaxed = true) + val microgLoginManager = mockk() + val anonymousLoginManager = mockk() val aasTokenConverter = mockk() val userProfileFetcher = mockk() val authData = AuthData(email = "user@gmail.com") - val profile = mockk() - 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() } 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, @@ -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 = MutableStateFlow(authDataJson) + override val emailData: MutableStateFlow = MutableStateFlow(email) + override val oauthToken: MutableStateFlow = MutableStateFlow(oauthToken) + override val aasToken: MutableStateFlow = MutableStateFlow(aasToken) + override val userType: MutableStateFlow = MutableStateFlow(initialUser.name) + override val playStoreAuthSource: MutableStateFlow = MutableStateFlow(playStoreAuthSource) + override val user: MutableStateFlow = MutableStateFlow(initialUser) + override val loginState: MutableStateFlow = MutableStateFlow(LoginState.AVAILABLE) + override val tocStatus: Flow = flowOf(false) + override val tosVersion: Flow = 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 } }