Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7bb7a8cf authored by Jonathan Klee's avatar Jonathan Klee
Browse files

Merge branch '0000-a15-fix-flakky-tests-ai-review' into 'main'

tests: fix flakky tests

See merge request !722
parents 6407b81c 96699b15
Loading
Loading
Loading
Loading
Loading
+136 −44
Original line number Diff line number Diff line
@@ -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<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,
@@ -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,
@@ -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,
@@ -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
    }
}