diff --git a/app/build.gradle b/app/build.gradle index 1d0b1c0d7b60aade5b80c883148325beb0929118..81ed596b62decc731526df08368be4cff27a8a7e 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -133,6 +133,13 @@ android { kotlin.sourceSets.all { languageSettings.optIn("kotlin.RequiresOptIn") } + + testOptions { + unitTests { + includeAndroidResources = true + returnDefaultValues = true + } + } } kapt { @@ -147,6 +154,8 @@ allOpen { dependencies { + implementation project(':modules') + // TODO: Add splitinstall-lib to a repo https://gitlab.e.foundation/e/os/backlog/-/issues/628 api files('libs/splitinstall-lib.jar') @@ -250,4 +259,9 @@ dependencies { // elib implementation 'foundation.e:elib:0.0.1-alpha11' + + testImplementation 'org.mockito:mockito-core:5.0.0' + testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0' + testImplementation 'org.robolectric:robolectric:4.9' + testImplementation 'org.json:json:20180813' // Added to avoid SystemInfoProvider.getAppBuildInfo() mock error } diff --git a/app/src/main/java/foundation/e/apps/di/LoginModule.kt b/app/src/main/java/foundation/e/apps/di/LoginModule.kt index f59a084b8abfbab057fc725a488be61096d43095..3df9857af423c883e61d7eaf867c8ad8a24cb7b8 100644 --- a/app/src/main/java/foundation/e/apps/di/LoginModule.kt +++ b/app/src/main/java/foundation/e/apps/di/LoginModule.kt @@ -24,6 +24,8 @@ import dagger.hilt.components.SingletonComponent import foundation.e.apps.data.login.LoginSourceCleanApk import foundation.e.apps.data.login.LoginSourceGPlay import foundation.e.apps.data.login.LoginSourceInterface +import foundation.e.apps.domain.login.repository.LoginRepositoryImpl +import foundation.e.apps.domain.login.usecase.UserLoginUseCase @InstallIn(SingletonComponent::class) @Module @@ -36,4 +38,11 @@ object LoginModule { ): List { return listOf(gPlay, cleanApk) } + + @Provides + fun provideLoginUserCase( + loginRepositoryImpl: LoginRepositoryImpl + ): UserLoginUseCase { + return UserLoginUseCase(loginRepositoryImpl) + } } diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt new file mode 100644 index 0000000000000000000000000000000000000000..45edb87738f3b98ebbcfe0066bf9aa6a6018204b --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepository.kt @@ -0,0 +1,26 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.repository + +import com.aurora.gplayapi.data.models.AuthData + +interface LoginRepository { + + suspend fun anonymousUser(): AuthData +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt new file mode 100644 index 0000000000000000000000000000000000000000..58e970348915c5112bd4105b06498653559bd8af --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/repository/LoginRepositoryImpl.kt @@ -0,0 +1,57 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.repository + +import android.content.Context +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.model.AnonymousAuthDataRequestBody +import app.lounge.networking.NetworkResult +import app.lounge.storage.cache.configurations +import com.aurora.gplayapi.data.models.AuthData +import dagger.hilt.android.qualifiers.ApplicationContext +import foundation.e.apps.utils.SystemInfoProvider +import java.util.Properties +import javax.inject.Inject + +class LoginRepositoryImpl @Inject constructor( + @ApplicationContext val applicationContext: Context, + private val properties: Properties, + private val anonymousUser: AnonymousUser, +) : LoginRepository { + + private val userAgent: String by lazy { SystemInfoProvider.getAppBuildInfo() } + + override suspend fun anonymousUser(): AuthData { + val result = anonymousUser.requestAuthData( + anonymousAuthDataRequestBody = AnonymousAuthDataRequestBody( + properties = properties, + userAgent = userAgent + ) + ) + + when (result) { + is NetworkResult.Error -> + throw Exception(result.errorMessage, result.exception) + is NetworkResult.Success -> { + applicationContext.configurations.authData = result.data.toString() + return result.data + } + } + } +} diff --git a/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt new file mode 100644 index 0000000000000000000000000000000000000000..9244b1444221359ff5d0e3536ef8f2d7a4e5c67c --- /dev/null +++ b/app/src/main/java/foundation/e/apps/domain/login/usecase/UserLoginUseCase.kt @@ -0,0 +1,41 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.usecase + +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.domain.login.repository.LoginRepository +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import javax.inject.Inject + +class UserLoginUseCase @Inject constructor( + private val loginRepository: LoginRepository, +) { + + fun anonymousUser(): Flow> = flow { + try { + emit(Resource.Loading()) + val userResponse = loginRepository.anonymousUser() + emit(Resource.Success(userResponse)) + } catch (e: Exception) { + emit(Resource.Error(e.localizedMessage)) + } + } +} diff --git a/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt new file mode 100644 index 0000000000000000000000000000000000000000..23f54bc343f977a11a681314eda371c56b9b2415 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginState.kt @@ -0,0 +1,25 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.presentation.login + +data class LoginState( + val isLoading: Boolean = false, + val isLoggedIn: Boolean = false, + val error: String = "" +) diff --git a/app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt similarity index 78% rename from app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt rename to app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt index 25c54362e6912ddeb2618291cd1908ce94b35b01..18da1ca6e4b7cb650ee9dc351b9267e9dfa8c315 100644 --- a/app/src/main/java/foundation/e/apps/data/login/LoginViewModel.kt +++ b/app/src/main/java/foundation/e/apps/presentation/login/LoginViewModel.kt @@ -15,14 +15,21 @@ * along with this program. If not, see . */ -package foundation.e.apps.data.login +package foundation.e.apps.presentation.login +import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.enums.User +import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.data.login.LoginSourceRepository +import foundation.e.apps.domain.login.usecase.UserLoginUseCase import foundation.e.apps.ui.parentFragment.LoadingViewModel +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import javax.inject.Inject @@ -33,6 +40,7 @@ import javax.inject.Inject @HiltViewModel class LoginViewModel @Inject constructor( private val loginSourceRepository: LoginSourceRepository, + private val userLoginUseCase: UserLoginUseCase ) : ViewModel() { /** @@ -131,4 +139,27 @@ class LoginViewModel @Inject constructor( authObjects.postValue(listOf()) } } + + private val _loginState: MutableLiveData = MutableLiveData() + val loginState: LiveData = _loginState + + fun authenticateAnonymousUser() { + viewModelScope.launch { + userLoginUseCase.anonymousUser().onEach { result -> + when (result) { + is Resource.Success -> { + _loginState.value = LoginState(isLoggedIn = true) + } + is Resource.Error -> { + _loginState.value = LoginState( + error = result.message ?: "An unexpected error occured" + ) + } + is Resource.Loading -> { + _loginState.value = LoginState(isLoading = true) + } + } + }.collect() + } + } } diff --git a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt index a2426b41f023fb3f26770c5dbb5ef2b061c65c0b..82e5f4ce19127d657fc6c2e636702b81702616e8 100644 --- a/app/src/main/java/foundation/e/apps/ui/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/ui/MainActivity.kt @@ -46,15 +46,14 @@ import foundation.e.apps.R import foundation.e.apps.data.enums.Status import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.databinding.ActivityMainBinding import foundation.e.apps.install.updates.UpdatesNotifier import foundation.e.apps.install.workmanager.InstallWorkManager +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.purchase.AppPurchaseFragmentDirections import foundation.e.apps.ui.settings.SettingsFragment -import foundation.e.apps.ui.setup.signin.SignInViewModel import foundation.e.apps.utils.SystemInfoProvider import foundation.e.apps.utils.eventBus.AppEvent import foundation.e.apps.utils.eventBus.EventBus @@ -68,7 +67,6 @@ import java.util.UUID @AndroidEntryPoint class MainActivity : AppCompatActivity() { - private lateinit var signInViewModel: SignInViewModel private lateinit var loginViewModel: LoginViewModel private lateinit var binding: ActivityMainBinding private val TAG = MainActivity::class.java.simpleName @@ -90,7 +88,6 @@ class MainActivity : AppCompatActivity() { var hasInternet = true viewModel = ViewModelProvider(this)[MainActivityViewModel::class.java] - signInViewModel = ViewModelProvider(this)[SignInViewModel::class.java] loginViewModel = ViewModelProvider(this)[LoginViewModel::class.java] // navOptions and activityNavController for TOS and SignIn Fragments diff --git a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt index 5173da6fecf6a5efdda1fd3fb097fcb0df58dd8a..9e395ae2a9fed384b97004c74fd91d560ced8f87 100644 --- a/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/parentFragment/TimeoutFragment.kt @@ -34,13 +34,13 @@ import foundation.e.apps.R import foundation.e.apps.data.enums.User import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.LoginSourceGPlay -import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.data.login.exceptions.CleanApkException import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.data.login.exceptions.UnknownSourceException import foundation.e.apps.databinding.DialogErrorLogBinding +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel import timber.log.Timber diff --git a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt index d3c507cad16681bb6ca6c907bcb3d24d3a156528..9865ff7cd66f5fa909381ad565a7e5ff66dc8c92 100644 --- a/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/settings/SettingsFragment.kt @@ -40,9 +40,9 @@ import foundation.e.apps.BuildConfig import foundation.e.apps.R import foundation.e.apps.data.enums.User import foundation.e.apps.data.fused.UpdatesDao -import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.databinding.CustomPreferenceBinding import foundation.e.apps.install.updates.UpdatesWorkManager +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.utils.SystemInfoProvider import timber.log.Timber diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt index 213e84148393d2c6c1a0965bddde44ce7cb9d5c9..e9ce89eb328229d13bb9af99d39a6ad4d9d20223 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/SignInFragment.kt @@ -7,9 +7,9 @@ import androidx.lifecycle.ViewModelProvider import androidx.navigation.findNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R -import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.databinding.FragmentSignInBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate +import foundation.e.apps.presentation.login.LoginViewModel import foundation.e.apps.utils.showGoogleSignInAlertDialog @AndroidEntryPoint diff --git a/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt b/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt index 8dc3081b306bee1790d961c2a2ed725765f8cf13..3ecc77843e4792036ea7e4bb96d11d2b30625130 100644 --- a/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/setup/signin/google/GoogleSignInFragment.kt @@ -32,9 +32,9 @@ import androidx.navigation.findNavController import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.gplay.utils.AC2DMUtil -import foundation.e.apps.data.login.LoginViewModel import foundation.e.apps.databinding.FragmentGoogleSigninBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate +import foundation.e.apps.presentation.login.LoginViewModel @AndroidEntryPoint class GoogleSignInFragment : Fragment(R.layout.fragment_google_signin) { diff --git a/app/src/main/java/foundation/e/apps/utils/Resource.kt b/app/src/main/java/foundation/e/apps/utils/Resource.kt new file mode 100644 index 0000000000000000000000000000000000000000..f7a20a46787a04ca98f7f6b0841c00bb7e14c473 --- /dev/null +++ b/app/src/main/java/foundation/e/apps/utils/Resource.kt @@ -0,0 +1,40 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.utils + +/** + * Class represents the different states of a resource for user case layer + */ +sealed class Resource(val data: T? = null, val message: String? = null) { + /** + * Represents a successful state of the resource with data. + * @param data The data associated with the resource. + */ + class Success(data: T) : Resource(data) + /** + * Represents an error state of the resource with an error message. + * @param message The error message associated with the resource. + */ + class Error(message: String, data: T? = null) : Resource(data, message) + + /** + * Represents a loading state of the resource. + */ + class Loading(data: T? = null) : Resource(data) +} diff --git a/app/src/test/java/foundation/e/apps/Shared.kt b/app/src/test/java/foundation/e/apps/Shared.kt new file mode 100644 index 0000000000000000000000000000000000000000..95f108bec6885b70965da612d97b6450c968c933 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/Shared.kt @@ -0,0 +1,35 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps + +import app.lounge.model.AnonymousAuthDataRequestBody +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.utils.SystemInfoProvider +import java.util.Properties + +const val testEmailAddress: String = "eOS@murena.io" +const val loginFailureMessage = "Fail to login" +val testFailureException: Exception = Exception(loginFailureMessage) + +val testAnonymousRequestData = AnonymousAuthDataRequestBody( + properties = Properties(), + userAgent = SystemInfoProvider.getAppBuildInfo() +) + +val testAnonymousResponseData = AuthData(testEmailAddress, "") diff --git a/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt b/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..5e9f368b07952b0fb45b1de44dfd61ed5007a51d --- /dev/null +++ b/app/src/test/java/foundation/e/apps/domain/login/repository/LoginRepositoryTest.kt @@ -0,0 +1,85 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.domain.login.repository + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import app.lounge.login.anonymous.AnonymousUser +import app.lounge.networking.NetworkResult +import foundation.e.apps.loginFailureMessage +import foundation.e.apps.testAnonymousRequestData +import foundation.e.apps.testAnonymousResponseData +import foundation.e.apps.testEmailAddress +import foundation.e.apps.testFailureException +import kotlinx.coroutines.test.runTest +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.robolectric.RobolectricTestRunner + +@RunWith(RobolectricTestRunner::class) +class LoginRepositoryTest { + + @Mock + lateinit var anonymousUser: AnonymousUser + + private lateinit var context: Context + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + context = ApplicationProvider.getApplicationContext() + } + + @Test + fun testOnSuccessReturnAuthData() = runTest { + Mockito.`when`( + anonymousUser.requestAuthData(testAnonymousRequestData) + ).thenReturn(NetworkResult.Success(testAnonymousResponseData)) + + val result = LoginRepositoryImpl(context, testAnonymousRequestData.properties, anonymousUser) + .anonymousUser() + + Assert.assertNotNull(result) + Assert.assertEquals(testEmailAddress, result.email) + } + + @Test + fun testOnFailureReturnErrorWithException() = runTest { + Mockito.`when`( + anonymousUser.requestAuthData(testAnonymousRequestData) + ).thenReturn( + NetworkResult.Error( + exception = testFailureException, + code = 1, + errorMessage = loginFailureMessage + ) + ) + runCatching { + LoginRepositoryImpl(context, testAnonymousRequestData.properties, anonymousUser) + .run { anonymousUser() } + }.onFailure { error -> + Assert.assertEquals(testFailureException.message, error.message) + } + } +} diff --git a/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..16b2d5d77d2899e389c1e2b75f9ada7d9bf4a734 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/presentation/login/LoginViewModelTest.kt @@ -0,0 +1,108 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package foundation.e.apps.presentation.login + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import foundation.e.apps.data.login.LoginSourceRepository +import foundation.e.apps.domain.login.usecase.UserLoginUseCase +import foundation.e.apps.loginFailureMessage +import foundation.e.apps.testAnonymousResponseData +import foundation.e.apps.util.getOrAwaitValue +import foundation.e.apps.utils.Resource +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.StandardTestDispatcher +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.junit.After +import org.junit.Assert +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations + +@OptIn(ExperimentalCoroutinesApi::class) +class LoginViewModelTest { + + private val testDispatcher = StandardTestDispatcher() + + @get:Rule + val rule = InstantTaskExecutorRule() + + @Mock + lateinit var mockUserLoginUseCase: UserLoginUseCase + + @Mock + lateinit var loginRepository: LoginSourceRepository + + @Before + fun setUp() { + MockitoAnnotations.openMocks(this) + Dispatchers.setMain(testDispatcher) + } + + @Test + fun testOnSuccessReturnLogInStateTrue() = runTest { + Mockito.`when`(mockUserLoginUseCase.anonymousUser()) + .thenReturn(flowOf(Resource.Success(testAnonymousResponseData))) + + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + loginViewModel.authenticateAnonymousUser() + testDispatcher.scheduler.advanceUntilIdle() + val result = loginViewModel.loginState.getOrAwaitValue() + Assert.assertEquals(true, result.isLoggedIn) + Assert.assertEquals(false, result.isLoading) + } + + @Test + fun testOnFailureReturnLogInStateFalseWithError() = runTest { + Mockito.`when`(mockUserLoginUseCase.anonymousUser()) + .thenReturn(flowOf(Resource.Error(loginFailureMessage, null))) + + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + loginViewModel.authenticateAnonymousUser() + testDispatcher.scheduler.advanceUntilIdle() + val result = loginViewModel.loginState.getOrAwaitValue() + Assert.assertEquals(false, result.isLoggedIn) + Assert.assertEquals(false, result.isLoading) + Assert.assertEquals(loginFailureMessage, result.error) + } + + @Test + fun testOnLoadingReturnLogInStateFalse() = runTest { + Mockito.`when`(mockUserLoginUseCase.anonymousUser()) + .thenReturn(flowOf(Resource.Loading(null))) + + val loginViewModel = LoginViewModel(loginRepository, mockUserLoginUseCase) + loginViewModel.authenticateAnonymousUser() + testDispatcher.scheduler.advanceUntilIdle() + val result = loginViewModel.loginState.getOrAwaitValue() + Assert.assertEquals(true, result.isLoading) + Assert.assertEquals(false, result.isLoggedIn) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } +} diff --git a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt index 494cf0c28ef656bd244607ac5b06fcf795c42e63..d12ea1fefaf52023b3a3e2b9b1a91bd93e28c8d1 100644 --- a/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt +++ b/modules/src/androidTest/java/app/lounge/AnonymousUserAPITest.kt @@ -75,9 +75,9 @@ class AnonymousUserAPITest { ) } -private const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" +const val testUserAgent: String = "{\"package\":\"foundation.e.apps.debug\",\"version\":\"2.5.5.debug\",\"device\":\"coral\",\"api\":32,\"os_version\":\"1.11-s-20230511288805-dev-coral\",\"build_id\":\"319e25cd.20230630224839\"}" -private val testSystemProperties = Properties().apply { +val testSystemProperties = Properties().apply { setProperty("UserReadableName", "coral-default") setProperty("Build.HARDWARE", "coral") setProperty("Build.RADIO", "g8150-00123-220402-B-8399852")