Loading app/src/test/java/foundation/e/apps/data/login/MicrogLoginManagerTest.kt +108 −2 Original line number Diff line number Diff line Loading @@ -29,9 +29,11 @@ import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.data.login.playstore.OauthAuthDataBuilder import foundation.e.apps.data.preference.AppLoungeDataStore import java.util.concurrent.TimeUnit import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue import org.junit.Ignore import org.junit.Test Loading @@ -49,7 +51,7 @@ import org.robolectric.annotation.Config @Config(sdk = [30]) class MicrogLoginManagerTest { private val context: Application = RuntimeEnvironment.getApplication() private val context = RuntimeEnvironment.getApplication() @Test @Ignore("Flaky under mock AccountManager; covered by error-path tests") Loading Loading @@ -196,9 +198,113 @@ class MicrogLoginManagerTest { assertTrue(result is MicrogLoginManager.FetchResult.Error) } private fun buildMicrogLoginManager(accountManager: AccountManager): MicrogLoginManager { @Test fun `login uses existing oauth token when refresh not needed`() = runBlocking { val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(emptyArray()) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("oauth")) val authData = com.aurora.gplayapi.data.models.AuthData(email = "email") whenever(oauthAuthDataBuilder.build("oauth")).thenReturn(authData) val result = buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() assertEquals(authData, result) } @Test fun `login refreshes token and saves when prefix token is present`() = runBlocking { val account = Account("user@gmail.com", MicrogCertUtil.GOOGLE_ACCOUNT_TYPE) val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(arrayOf(account)) whenever( accountManager.getAuthToken( eq(account), eq(MicrogCertUtil.PLAY_AUTH_SCOPE), any<Bundle>(), eq(false), isNull(), isNull() ) ).thenReturn(ImmediateAccountManagerFuture(Bundle().apply { putString(AccountManager.KEY_AUTHTOKEN, "token123") })) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("ya29.old")) whenever(appLoungeDataStore.emailData).thenReturn(flowOf(account.name)) val authData = com.aurora.gplayapi.data.models.AuthData(email = account.name) whenever(oauthAuthDataBuilder.build("token123")).thenReturn(authData) val result = buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() assertEquals(authData, result) org.mockito.kotlin.verify(accountManager).invalidateAuthToken(account.type, "ya29.old") org.mockito.kotlin.verify(appLoungeDataStore).saveGoogleLogin(account.name, "token123") org.mockito.kotlin.verify(appLoungeDataStore).saveAasToken("") } @Test fun `login throws when token missing`() { val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(emptyArray()) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("")) val exception = assertThrows(IllegalStateException::class.java) { runBlocking { buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() } } assertEquals(MicrogLoginManager.MICROG_TOKEN_MISSING, exception.message) } @Test fun `login throws when refresh requires user action`() { val account = Account("user@gmail.com", MicrogCertUtil.GOOGLE_ACCOUNT_TYPE) val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(arrayOf(account)) val intent = Intent("foundation.e.apps.ACTION_LOGIN") val resultBundle = Bundle().apply { putParcelable(AccountManager.KEY_INTENT, intent) classLoader = Intent::class.java.classLoader } whenever( accountManager.getAuthToken( eq(account), eq(MicrogCertUtil.PLAY_AUTH_SCOPE), any<Bundle>(), eq(false), isNull(), isNull() ) ).thenReturn(ImmediateAccountManagerFuture(resultBundle)) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("ya29.old")) whenever(appLoungeDataStore.emailData).thenReturn(flowOf(account.name)) val exception = assertThrows(IllegalStateException::class.java) { runBlocking { buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() } } assertEquals(MicrogLoginManager.MICROG_TOKEN_REFRESH_FAILURE, exception.message) } private fun buildMicrogLoginManager( accountManager: AccountManager, oauthAuthDataBuilder: OauthAuthDataBuilder = mock(), appLoungeDataStore: AppLoungeDataStore = mock() ): MicrogLoginManager { return MicrogLoginManager(context, accountManager, oauthAuthDataBuilder, appLoungeDataStore) } Loading app/src/test/java/foundation/e/apps/data/login/playstore/OauthAuthDataBuilderTest.kt 0 → 100644 +51 −0 Original line number Diff line number Diff line package foundation.e.apps.data.login.playstore import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.helpers.AuthHelper import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.preference.AppLoungeDataStore import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject import io.mockk.unmockkObject import org.junit.After import org.junit.Before import org.junit.Test import java.util.Properties class OauthAuthDataBuilderTest { @Before fun setup() { mockkObject(AuthHelper) } @After fun tearDown() { unmockkObject(AuthHelper) } @Test fun build_usesEmailAndOauthToken() = runTest { val appLoungeDataStore = mockk<AppLoungeDataStore>() val properties = Properties() every { appLoungeDataStore.emailData } returns flowOf("user@gmail.com") val expected = AuthData(email = "user@gmail.com") every { AuthHelper.build( email = "user@gmail.com", token = "oauth-token", tokenType = AuthHelper.Token.AUTH, isAnonymous = false, properties = properties ) } returns expected val builder = OauthAuthDataBuilder(appLoungeDataStore, properties) val result = builder.build("oauth-token") assertThat(result).isEqualTo(expected) } } app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt 0 → 100644 +148 −0 Original line number Diff line number Diff line package foundation.e.apps.data.login.playstore import android.content.Context import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.data.models.UserProfile import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.core.AuthObject import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference import kotlinx.coroutines.flow.flowOf import io.mockk.coEvery import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test class PlayStoreAuthenticatorTest { @Test fun login_returnsSavedAuthWhenValid() = runTest { val context = mockk<Context>(relaxed = true) val appLoungeDataStore = mockk<AppLoungeDataStore>() val appLoungePreference = mockk<AppLoungePreference>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val authDataVerifier = mockk<PlayStoreAuthDataVerifier>() 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") val playResponse = mockk<PlayResponse>() every { playResponse.isSuccessful } returns true every { appLoungeDataStore.getUser() } returns User.ANONYMOUS every { authDataCache.getSavedAuthData() } returns savedAuth coEvery { authDataVerifier.validate(savedAuth) } returns playResponse every { authDataCache.formatAuthData(savedAuth) } returns savedAuth val authenticator = PlayStoreAuthenticator( context = context, appLoungeDataStore = appLoungeDataStore, appLoungePreference = appLoungePreference, authDataCache = authDataCache, authDataVerifier = authDataVerifier, googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, aasTokenConverter = aasTokenConverter, userProfileFetcher = userProfileFetcher ) val result = authenticator.login() 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) } @Test fun login_usesAnonymousLoginWhenNoSavedAuth() = runTest { val context = mockk<Context>(relaxed = true) val appLoungeDataStore = mockk<AppLoungeDataStore>() val appLoungePreference = mockk<AppLoungePreference>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val authDataVerifier = mockk<PlayStoreAuthDataVerifier>() val googleLoginManager = mockk<GoogleLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>(relaxed = true) val userProfileFetcher = mockk<UserProfileFetcher>(relaxed = true) val authData = AuthData(email = "anon@example.com", isAnonymous = true) every { appLoungeDataStore.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, appLoungeDataStore = appLoungeDataStore, appLoungePreference = appLoungePreference, authDataCache = authDataCache, authDataVerifier = authDataVerifier, googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, aasTokenConverter = aasTokenConverter, userProfileFetcher = userProfileFetcher ) val result = authenticator.login() 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) } @Test fun login_fetchesUserProfileForGoogle() = runTest { val context = mockk<Context>(relaxed = true) val appLoungeDataStore = mockk<AppLoungeDataStore>() val appLoungePreference = mockk<AppLoungePreference>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val authDataVerifier = mockk<PlayStoreAuthDataVerifier>() val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>(relaxed = true) val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authData = AuthData(email = "user@gmail.com") val profile = mockk<UserProfile>() every { appLoungeDataStore.getUser() } returns User.GOOGLE every { appLoungeDataStore.playStoreAuthSource } returns flowOf("GOOGLE") every { appLoungeDataStore.aasToken } returns flowOf("aas-token") every { authDataCache.getSavedAuthData() } returns null coEvery { googleLoginManager.login() } returns authData every { authDataCache.formatAuthData(any()) } answers { firstArg() } coEvery { userProfileFetcher.fetch(authData) } returns profile val authenticator = PlayStoreAuthenticator( context = context, appLoungeDataStore = appLoungeDataStore, appLoungePreference = appLoungePreference, authDataCache = authDataCache, authDataVerifier = authDataVerifier, googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, aasTokenConverter = aasTokenConverter, userProfileFetcher = userProfileFetcher ) val result = authenticator.login() val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result.data?.userProfile).isEqualTo(profile) } } app/src/test/java/foundation/e/apps/data/login/playstore/UserProfileFetcherTest.kt 0 → 100644 +59 −0 Original line number Diff line number Diff line package foundation.e.apps.data.login.playstore import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.UserProfile import com.aurora.gplayapi.helpers.UserProfileHelper import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.playstore.utils.GPlayHttpClient import io.mockk.every import io.mockk.mockk import io.mockk.mockkConstructor import io.mockk.unmockkConstructor import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test class UserProfileFetcherTest { @Before fun setup() { mockkConstructor(UserProfileHelper::class) } @After fun tearDown() { unmockkConstructor(UserProfileHelper::class) } @Test fun fetch_returnsProfileOnSuccess() = runTest { val authData = AuthData(email = "user@gmail.com") val gPlayHttpClient = mockk<GPlayHttpClient>() val profile = mockk<UserProfile>() every { anyConstructed<UserProfileHelper>().using(gPlayHttpClient) } answers { this.invocation.self as UserProfileHelper } every { anyConstructed<UserProfileHelper>().getUserProfile() } returns profile val fetcher = UserProfileFetcher(gPlayHttpClient) val result = fetcher.fetch(authData) assertThat(result).isEqualTo(profile) } @Test fun fetch_returnsNullOnFailure() = runTest { val authData = AuthData(email = "user@gmail.com") val gPlayHttpClient = mockk<GPlayHttpClient>() every { anyConstructed<UserProfileHelper>().using(gPlayHttpClient) } answers { this.invocation.self as UserProfileHelper } every { anyConstructed<UserProfileHelper>().getUserProfile() } throws IllegalStateException("boom") val fetcher = UserProfileFetcher(gPlayHttpClient) val result = fetcher.fetch(authData) assertThat(result).isNull() } } app/src/test/java/foundation/e/apps/data/playstore/PlayStoreRepositoryTest.kt +87 −1 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import androidx.test.core.app.ApplicationProvider import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.helpers.AppDetailsHelper import com.aurora.gplayapi.exceptions.InternalException import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.ApplicationDataManager Loading Loading @@ -60,7 +61,7 @@ class PlayStoreRepositoryTest { @Test fun `getAppDetails retries after auth refresh on unauthorized`() = runTest { val authData = AuthData(email = "user@example.com") val authData = AuthData(email = "user@gmail.com") val app = App(packageName = "pkg.test", versionCode = 2) val authenticatorRepository = mock<AuthenticatorRepository>() Loading Loading @@ -113,4 +114,89 @@ class PlayStoreRepositoryTest { mockitoVerify(authenticatorRepository).fetchAuthObjects(listOf(StoreType.PLAY_STORE)) verify(exactly = 2) { anyConstructed<AppDetailsHelper>().getAppByPackageName("pkg.test") } } @Test fun `getAppDetails throws AppNotFound on 404`() = runTest { val authData = AuthData(email = "user@gmail.com") val authenticatorRepository = mock<AuthenticatorRepository>() repository = PlayStoreRepository( context = context, gPlayHttpClient = gPlayHttpClient, authenticatorRepository = authenticatorRepository, applicationDataManager = applicationDataManager, playStoreSearchHelper = playStoreSearchHelper ) whenever(authenticatorRepository.getGPlayAuthOrThrow()).thenReturn(authData) every { anyConstructed<AppDetailsHelper>().getAppByPackageName("missing.pkg") } throws GplayHttpRequestException(404, "not found") val exception = kotlin.test.assertFailsWith<InternalException.AppNotFound> { repository.getAppDetails("missing.pkg") } assertThat(exception).isInstanceOf(InternalException.AppNotFound::class.java) } @Test fun `getAppsDetails refreshes auth when anonymous and all versions zero`() = runTest { val authData = AuthData(email = "anon@example.com", isAnonymous = true) val staleApps = listOf(App(packageName = "pkg.test", versionCode = 0)) val refreshedApps = listOf(App(packageName = "pkg.test", versionCode = 9)) val authenticatorRepository = mock<AuthenticatorRepository>() repository = PlayStoreRepository( context = context, gPlayHttpClient = gPlayHttpClient, authenticatorRepository = authenticatorRepository, applicationDataManager = applicationDataManager, playStoreSearchHelper = playStoreSearchHelper ) whenever(authenticatorRepository.getGPlayAuthOrThrow()).thenReturn(authData) whenever(authenticatorRepository.fetchAuthObjects(listOf(StoreType.PLAY_STORE))) .thenReturn(emptyList()) every { anyConstructed<AppDetailsHelper>().getAppByPackageName(listOf("pkg.test")) } returns staleApps andThen refreshedApps val result = repository.getAppsDetails(listOf("pkg.test")) assertThat(result).hasSize(1) mockitoVerify(authenticatorRepository).fetchAuthObjects(listOf(StoreType.PLAY_STORE)) verify(exactly = 2) { anyConstructed<AppDetailsHelper>().getAppByPackageName(listOf("pkg.test")) } } @Test fun `getAppsDetails filters invalid apps`() = runTest { val authData = AuthData(email = "user@gmail.com") val apps = listOf( App(packageName = "", versionCode = 3), App(packageName = "valid.pkg", versionCode = 0), App(packageName = "valid.pkg", versionCode = 5) ) val authenticatorRepository = mock<AuthenticatorRepository>() repository = PlayStoreRepository( context = context, gPlayHttpClient = gPlayHttpClient, authenticatorRepository = authenticatorRepository, applicationDataManager = applicationDataManager, playStoreSearchHelper = playStoreSearchHelper ) whenever(authenticatorRepository.getGPlayAuthOrThrow()).thenReturn(authData) every { anyConstructed<AppDetailsHelper>().getAppByPackageName(listOf("valid.pkg")) } returns apps val result = repository.getAppsDetails(listOf("valid.pkg")) assertThat(result).hasSize(1) assertThat(result.first().package_name).isEqualTo("valid.pkg") } } Loading
app/src/test/java/foundation/e/apps/data/login/MicrogLoginManagerTest.kt +108 −2 Original line number Diff line number Diff line Loading @@ -29,9 +29,11 @@ import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.data.login.playstore.OauthAuthDataBuilder import foundation.e.apps.data.preference.AppLoungeDataStore import java.util.concurrent.TimeUnit import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertThrows import org.junit.Assert.assertTrue import org.junit.Ignore import org.junit.Test Loading @@ -49,7 +51,7 @@ import org.robolectric.annotation.Config @Config(sdk = [30]) class MicrogLoginManagerTest { private val context: Application = RuntimeEnvironment.getApplication() private val context = RuntimeEnvironment.getApplication() @Test @Ignore("Flaky under mock AccountManager; covered by error-path tests") Loading Loading @@ -196,9 +198,113 @@ class MicrogLoginManagerTest { assertTrue(result is MicrogLoginManager.FetchResult.Error) } private fun buildMicrogLoginManager(accountManager: AccountManager): MicrogLoginManager { @Test fun `login uses existing oauth token when refresh not needed`() = runBlocking { val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(emptyArray()) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("oauth")) val authData = com.aurora.gplayapi.data.models.AuthData(email = "email") whenever(oauthAuthDataBuilder.build("oauth")).thenReturn(authData) val result = buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() assertEquals(authData, result) } @Test fun `login refreshes token and saves when prefix token is present`() = runBlocking { val account = Account("user@gmail.com", MicrogCertUtil.GOOGLE_ACCOUNT_TYPE) val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(arrayOf(account)) whenever( accountManager.getAuthToken( eq(account), eq(MicrogCertUtil.PLAY_AUTH_SCOPE), any<Bundle>(), eq(false), isNull(), isNull() ) ).thenReturn(ImmediateAccountManagerFuture(Bundle().apply { putString(AccountManager.KEY_AUTHTOKEN, "token123") })) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("ya29.old")) whenever(appLoungeDataStore.emailData).thenReturn(flowOf(account.name)) val authData = com.aurora.gplayapi.data.models.AuthData(email = account.name) whenever(oauthAuthDataBuilder.build("token123")).thenReturn(authData) val result = buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() assertEquals(authData, result) org.mockito.kotlin.verify(accountManager).invalidateAuthToken(account.type, "ya29.old") org.mockito.kotlin.verify(appLoungeDataStore).saveGoogleLogin(account.name, "token123") org.mockito.kotlin.verify(appLoungeDataStore).saveAasToken("") } @Test fun `login throws when token missing`() { val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(emptyArray()) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("")) val exception = assertThrows(IllegalStateException::class.java) { runBlocking { buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() } } assertEquals(MicrogLoginManager.MICROG_TOKEN_MISSING, exception.message) } @Test fun `login throws when refresh requires user action`() { val account = Account("user@gmail.com", MicrogCertUtil.GOOGLE_ACCOUNT_TYPE) val accountManager = mock<AccountManager>() whenever(accountManager.getAccountsByType(eq(MicrogCertUtil.GOOGLE_ACCOUNT_TYPE))) .thenReturn(arrayOf(account)) val intent = Intent("foundation.e.apps.ACTION_LOGIN") val resultBundle = Bundle().apply { putParcelable(AccountManager.KEY_INTENT, intent) classLoader = Intent::class.java.classLoader } whenever( accountManager.getAuthToken( eq(account), eq(MicrogCertUtil.PLAY_AUTH_SCOPE), any<Bundle>(), eq(false), isNull(), isNull() ) ).thenReturn(ImmediateAccountManagerFuture(resultBundle)) val oauthAuthDataBuilder = mock<OauthAuthDataBuilder>() val appLoungeDataStore = mock<AppLoungeDataStore>() whenever(appLoungeDataStore.oauthToken).thenReturn(flowOf("ya29.old")) whenever(appLoungeDataStore.emailData).thenReturn(flowOf(account.name)) val exception = assertThrows(IllegalStateException::class.java) { runBlocking { buildMicrogLoginManager(accountManager, oauthAuthDataBuilder, appLoungeDataStore).login() } } assertEquals(MicrogLoginManager.MICROG_TOKEN_REFRESH_FAILURE, exception.message) } private fun buildMicrogLoginManager( accountManager: AccountManager, oauthAuthDataBuilder: OauthAuthDataBuilder = mock(), appLoungeDataStore: AppLoungeDataStore = mock() ): MicrogLoginManager { return MicrogLoginManager(context, accountManager, oauthAuthDataBuilder, appLoungeDataStore) } Loading
app/src/test/java/foundation/e/apps/data/login/playstore/OauthAuthDataBuilderTest.kt 0 → 100644 +51 −0 Original line number Diff line number Diff line package foundation.e.apps.data.login.playstore import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.helpers.AuthHelper import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.preference.AppLoungeDataStore import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.runTest import io.mockk.every import io.mockk.mockk import io.mockk.mockkObject import io.mockk.unmockkObject import org.junit.After import org.junit.Before import org.junit.Test import java.util.Properties class OauthAuthDataBuilderTest { @Before fun setup() { mockkObject(AuthHelper) } @After fun tearDown() { unmockkObject(AuthHelper) } @Test fun build_usesEmailAndOauthToken() = runTest { val appLoungeDataStore = mockk<AppLoungeDataStore>() val properties = Properties() every { appLoungeDataStore.emailData } returns flowOf("user@gmail.com") val expected = AuthData(email = "user@gmail.com") every { AuthHelper.build( email = "user@gmail.com", token = "oauth-token", tokenType = AuthHelper.Token.AUTH, isAnonymous = false, properties = properties ) } returns expected val builder = OauthAuthDataBuilder(appLoungeDataStore, properties) val result = builder.build("oauth-token") assertThat(result).isEqualTo(expected) } }
app/src/test/java/foundation/e/apps/data/login/playstore/PlayStoreAuthenticatorTest.kt 0 → 100644 +148 −0 Original line number Diff line number Diff line package foundation.e.apps.data.login.playstore import android.content.Context import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.data.models.UserProfile import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.core.AuthObject import foundation.e.apps.data.login.microg.MicrogLoginManager import foundation.e.apps.data.preference.AppLoungeDataStore import foundation.e.apps.data.preference.AppLoungePreference import kotlinx.coroutines.flow.flowOf import io.mockk.coEvery import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest import org.junit.Test class PlayStoreAuthenticatorTest { @Test fun login_returnsSavedAuthWhenValid() = runTest { val context = mockk<Context>(relaxed = true) val appLoungeDataStore = mockk<AppLoungeDataStore>() val appLoungePreference = mockk<AppLoungePreference>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val authDataVerifier = mockk<PlayStoreAuthDataVerifier>() 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") val playResponse = mockk<PlayResponse>() every { playResponse.isSuccessful } returns true every { appLoungeDataStore.getUser() } returns User.ANONYMOUS every { authDataCache.getSavedAuthData() } returns savedAuth coEvery { authDataVerifier.validate(savedAuth) } returns playResponse every { authDataCache.formatAuthData(savedAuth) } returns savedAuth val authenticator = PlayStoreAuthenticator( context = context, appLoungeDataStore = appLoungeDataStore, appLoungePreference = appLoungePreference, authDataCache = authDataCache, authDataVerifier = authDataVerifier, googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, aasTokenConverter = aasTokenConverter, userProfileFetcher = userProfileFetcher ) val result = authenticator.login() 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) } @Test fun login_usesAnonymousLoginWhenNoSavedAuth() = runTest { val context = mockk<Context>(relaxed = true) val appLoungeDataStore = mockk<AppLoungeDataStore>() val appLoungePreference = mockk<AppLoungePreference>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val authDataVerifier = mockk<PlayStoreAuthDataVerifier>() val googleLoginManager = mockk<GoogleLoginManager>(relaxed = true) val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>() val aasTokenConverter = mockk<OauthToAasTokenConverter>(relaxed = true) val userProfileFetcher = mockk<UserProfileFetcher>(relaxed = true) val authData = AuthData(email = "anon@example.com", isAnonymous = true) every { appLoungeDataStore.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, appLoungeDataStore = appLoungeDataStore, appLoungePreference = appLoungePreference, authDataCache = authDataCache, authDataVerifier = authDataVerifier, googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, aasTokenConverter = aasTokenConverter, userProfileFetcher = userProfileFetcher ) val result = authenticator.login() 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) } @Test fun login_fetchesUserProfileForGoogle() = runTest { val context = mockk<Context>(relaxed = true) val appLoungeDataStore = mockk<AppLoungeDataStore>() val appLoungePreference = mockk<AppLoungePreference>(relaxed = true) val authDataCache = mockk<AuthDataCache>() val authDataVerifier = mockk<PlayStoreAuthDataVerifier>() val googleLoginManager = mockk<GoogleLoginManager>() val microgLoginManager = mockk<MicrogLoginManager>(relaxed = true) val anonymousLoginManager = mockk<AnonymousLoginManager>(relaxed = true) val aasTokenConverter = mockk<OauthToAasTokenConverter>() val userProfileFetcher = mockk<UserProfileFetcher>() val authData = AuthData(email = "user@gmail.com") val profile = mockk<UserProfile>() every { appLoungeDataStore.getUser() } returns User.GOOGLE every { appLoungeDataStore.playStoreAuthSource } returns flowOf("GOOGLE") every { appLoungeDataStore.aasToken } returns flowOf("aas-token") every { authDataCache.getSavedAuthData() } returns null coEvery { googleLoginManager.login() } returns authData every { authDataCache.formatAuthData(any()) } answers { firstArg() } coEvery { userProfileFetcher.fetch(authData) } returns profile val authenticator = PlayStoreAuthenticator( context = context, appLoungeDataStore = appLoungeDataStore, appLoungePreference = appLoungePreference, authDataCache = authDataCache, authDataVerifier = authDataVerifier, googleLoginManager = googleLoginManager, microgLoginManager = microgLoginManager, anonymousLoginManager = anonymousLoginManager, aasTokenConverter = aasTokenConverter, userProfileFetcher = userProfileFetcher ) val result = authenticator.login() val authObject = result.authObject as AuthObject.GPlayAuth assertThat(authObject.result.data?.userProfile).isEqualTo(profile) } }
app/src/test/java/foundation/e/apps/data/login/playstore/UserProfileFetcherTest.kt 0 → 100644 +59 −0 Original line number Diff line number Diff line package foundation.e.apps.data.login.playstore import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.UserProfile import com.aurora.gplayapi.helpers.UserProfileHelper import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.playstore.utils.GPlayHttpClient import io.mockk.every import io.mockk.mockk import io.mockk.mockkConstructor import io.mockk.unmockkConstructor import kotlinx.coroutines.test.runTest import org.junit.After import org.junit.Before import org.junit.Test class UserProfileFetcherTest { @Before fun setup() { mockkConstructor(UserProfileHelper::class) } @After fun tearDown() { unmockkConstructor(UserProfileHelper::class) } @Test fun fetch_returnsProfileOnSuccess() = runTest { val authData = AuthData(email = "user@gmail.com") val gPlayHttpClient = mockk<GPlayHttpClient>() val profile = mockk<UserProfile>() every { anyConstructed<UserProfileHelper>().using(gPlayHttpClient) } answers { this.invocation.self as UserProfileHelper } every { anyConstructed<UserProfileHelper>().getUserProfile() } returns profile val fetcher = UserProfileFetcher(gPlayHttpClient) val result = fetcher.fetch(authData) assertThat(result).isEqualTo(profile) } @Test fun fetch_returnsNullOnFailure() = runTest { val authData = AuthData(email = "user@gmail.com") val gPlayHttpClient = mockk<GPlayHttpClient>() every { anyConstructed<UserProfileHelper>().using(gPlayHttpClient) } answers { this.invocation.self as UserProfileHelper } every { anyConstructed<UserProfileHelper>().getUserProfile() } throws IllegalStateException("boom") val fetcher = UserProfileFetcher(gPlayHttpClient) val result = fetcher.fetch(authData) assertThat(result).isNull() } }
app/src/test/java/foundation/e/apps/data/playstore/PlayStoreRepositoryTest.kt +87 −1 Original line number Diff line number Diff line Loading @@ -6,6 +6,7 @@ import androidx.test.core.app.ApplicationProvider import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.helpers.AppDetailsHelper import com.aurora.gplayapi.exceptions.InternalException import com.google.common.truth.Truth.assertThat import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.ApplicationDataManager Loading Loading @@ -60,7 +61,7 @@ class PlayStoreRepositoryTest { @Test fun `getAppDetails retries after auth refresh on unauthorized`() = runTest { val authData = AuthData(email = "user@example.com") val authData = AuthData(email = "user@gmail.com") val app = App(packageName = "pkg.test", versionCode = 2) val authenticatorRepository = mock<AuthenticatorRepository>() Loading Loading @@ -113,4 +114,89 @@ class PlayStoreRepositoryTest { mockitoVerify(authenticatorRepository).fetchAuthObjects(listOf(StoreType.PLAY_STORE)) verify(exactly = 2) { anyConstructed<AppDetailsHelper>().getAppByPackageName("pkg.test") } } @Test fun `getAppDetails throws AppNotFound on 404`() = runTest { val authData = AuthData(email = "user@gmail.com") val authenticatorRepository = mock<AuthenticatorRepository>() repository = PlayStoreRepository( context = context, gPlayHttpClient = gPlayHttpClient, authenticatorRepository = authenticatorRepository, applicationDataManager = applicationDataManager, playStoreSearchHelper = playStoreSearchHelper ) whenever(authenticatorRepository.getGPlayAuthOrThrow()).thenReturn(authData) every { anyConstructed<AppDetailsHelper>().getAppByPackageName("missing.pkg") } throws GplayHttpRequestException(404, "not found") val exception = kotlin.test.assertFailsWith<InternalException.AppNotFound> { repository.getAppDetails("missing.pkg") } assertThat(exception).isInstanceOf(InternalException.AppNotFound::class.java) } @Test fun `getAppsDetails refreshes auth when anonymous and all versions zero`() = runTest { val authData = AuthData(email = "anon@example.com", isAnonymous = true) val staleApps = listOf(App(packageName = "pkg.test", versionCode = 0)) val refreshedApps = listOf(App(packageName = "pkg.test", versionCode = 9)) val authenticatorRepository = mock<AuthenticatorRepository>() repository = PlayStoreRepository( context = context, gPlayHttpClient = gPlayHttpClient, authenticatorRepository = authenticatorRepository, applicationDataManager = applicationDataManager, playStoreSearchHelper = playStoreSearchHelper ) whenever(authenticatorRepository.getGPlayAuthOrThrow()).thenReturn(authData) whenever(authenticatorRepository.fetchAuthObjects(listOf(StoreType.PLAY_STORE))) .thenReturn(emptyList()) every { anyConstructed<AppDetailsHelper>().getAppByPackageName(listOf("pkg.test")) } returns staleApps andThen refreshedApps val result = repository.getAppsDetails(listOf("pkg.test")) assertThat(result).hasSize(1) mockitoVerify(authenticatorRepository).fetchAuthObjects(listOf(StoreType.PLAY_STORE)) verify(exactly = 2) { anyConstructed<AppDetailsHelper>().getAppByPackageName(listOf("pkg.test")) } } @Test fun `getAppsDetails filters invalid apps`() = runTest { val authData = AuthData(email = "user@gmail.com") val apps = listOf( App(packageName = "", versionCode = 3), App(packageName = "valid.pkg", versionCode = 0), App(packageName = "valid.pkg", versionCode = 5) ) val authenticatorRepository = mock<AuthenticatorRepository>() repository = PlayStoreRepository( context = context, gPlayHttpClient = gPlayHttpClient, authenticatorRepository = authenticatorRepository, applicationDataManager = applicationDataManager, playStoreSearchHelper = playStoreSearchHelper ) whenever(authenticatorRepository.getGPlayAuthOrThrow()).thenReturn(authData) every { anyConstructed<AppDetailsHelper>().getAppByPackageName(listOf("valid.pkg")) } returns apps val result = repository.getAppsDetails(listOf("valid.pkg")) assertThat(result).hasSize(1) assertThat(result.first().package_name).isEqualTo("valid.pkg") } }