From 8babc8da9e1ad3e6534433cd4c287bbc688413d5 Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Tue, 12 Sep 2023 23:33:17 +0600 Subject: [PATCH 1/2] added unit tests for unauthorized response --- .../apps/data/gplay/utils/GPlayHttpClient.kt | 2 +- .../foundation/e/apps/FusedApiImplTest.kt | 2 +- .../foundation/e/apps/GplyHttpClientTest.kt | 132 ++++++++++++++++++ .../foundation/e/apps/NetworkHandlerTest.kt | 9 ++ .../java/foundation/e/apps/util/FakeCall.kt | 79 +++++++++++ 5 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt create mode 100644 app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt create mode 100644 app/src/test/java/foundation/e/apps/util/FakeCall.kt diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt index acb44e965..6c2929048 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt @@ -61,7 +61,7 @@ class GPlayHttpClient @Inject constructor( const val STATUS_CODE_TIMEOUT = 408 } - private val okHttpClient = OkHttpClient().newBuilder() + var okHttpClient = OkHttpClient().newBuilder() .retryOnConnectionFailure(false) .callTimeout(HTTP_TIMEOUT_IN_SECOND, TimeUnit.SECONDS) .followRedirects(true) diff --git a/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt b/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt index 3096c444c..c8e8e98aa 100644 --- a/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt @@ -682,7 +682,7 @@ class FusedApiImplTest { Mockito.`when`( gPlayAPIRepository.getCategories(CategoryType.APPLICATION) - ).thenThrow(RuntimeException()) + ).thenThrow() val categoryListResponse = fusedAPIImpl.getCategoriesList(CategoryType.APPLICATION) diff --git a/app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt b/app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt new file mode 100644 index 000000000..8593cffde --- /dev/null +++ b/app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt @@ -0,0 +1,132 @@ +/* + * 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 com.aurora.gplayapi.data.models.PlayResponse +import foundation.e.apps.data.gplay.utils.GPlayHttpClient +import foundation.e.apps.util.FakeCall +import foundation.e.apps.util.MainCoroutineRule +import foundation.e.apps.utils.SystemInfoProvider +import foundation.e.apps.utils.eventBus.AppEvent +import foundation.e.apps.utils.eventBus.EventBus +import io.mockk.every +import io.mockk.mockkObject +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import okhttp3.Cache +import okhttp3.OkHttpClient +import okhttp3.RequestBody.Companion.toRequestBody +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue + +@OptIn(ExperimentalCoroutinesApi::class) +class GplyHttpClientTest { + + @Mock + private lateinit var cache: Cache + + @Mock + private lateinit var okHttpClient: OkHttpClient + + private lateinit var call: FakeCall + + private lateinit var gPlayHttpClient: GPlayHttpClient + + @OptIn(ExperimentalCoroutinesApi::class) + @get:Rule + val coroutineTestRule = MainCoroutineRule() + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + gPlayHttpClient = GPlayHttpClient(cache) + gPlayHttpClient.okHttpClient = this.okHttpClient + call = FakeCall() + } + + @Test + fun testPostMapFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.post("http://abc.abc", mapOf(), mapOf()) + assertResponse(response) + } + + @Test + fun testPostRequestBodyFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.post("http://abc.abc", mapOf(), "".toRequestBody()) + assertResponse(response) + } + + @Test + fun testPostByteArrayRequestBodyFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.post("http://abc.abc", mapOf(), "".toByteArray()) + assertResponse(response) + } + + @Test + fun testGetFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.get(FakeCall.FAKE_URL, mapOf()) + assertResponse(response) + } + + @Test + fun testGetParamStringFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.get(FakeCall.FAKE_URL, mapOf(), "") + assertResponse(response) + } + + @Test + fun testGetMapFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.get(FakeCall.FAKE_URL, mapOf(), mapOf()) + assertResponse(response) + } + + @Test + fun testPostAuthFailWhenStatus401() = runTest { + initMocks() + val response = gPlayHttpClient.postAuth("http://abc.abc", "".toByteArray()) + assertResponse(response) + } + + private fun initMocks() { + call.willThrow401 = true + mockkObject(SystemInfoProvider) + every { SystemInfoProvider.getAppBuildInfo() } returns "" + Mockito.`when`(okHttpClient.newCall(any())).thenReturn(call) + } + private suspend fun assertResponse(response: PlayResponse) { + assertFalse(response.isSuccessful) + assertTrue(response.code == 401) + assertTrue(EventBus.events.first() is AppEvent.InvalidAuthEvent) + } + +} diff --git a/app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt b/app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt new file mode 100644 index 000000000..9912cb11a --- /dev/null +++ b/app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt @@ -0,0 +1,9 @@ +package foundation.e.apps + +import foundation.e.apps.data.handleNetworkResult + +class NetworkHandlerTest { + + fun testHandleNetworkResultWhenStatus401() { + } +} \ No newline at end of file diff --git a/app/src/test/java/foundation/e/apps/util/FakeCall.kt b/app/src/test/java/foundation/e/apps/util/FakeCall.kt new file mode 100644 index 000000000..1c6aa71d8 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/util/FakeCall.kt @@ -0,0 +1,79 @@ +/* + * 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.util + +import okhttp3.Call +import okhttp3.Callback +import okhttp3.Protocol +import okhttp3.Request +import okhttp3.Response +import okhttp3.ResponseBody +import okhttp3.ResponseBody.Companion.toResponseBody +import okio.Timeout + +class FakeCall : Call { + + var willThrow401 = false + + companion object { + const val FAKE_URL = "https://abc.abc" + } + + private val fakeRequest = Request.Builder().url(FAKE_URL).build() + override fun cancel() { + TODO("Not yet implemented") + } + + override fun clone(): Call { + TODO("Not yet implemented") + } + + override fun enqueue(responseCallback: Callback) { + TODO("Not yet implemented") + } + + override fun execute(): Response { + if (willThrow401) { + return Response.Builder() + .request(fakeRequest) + .protocol(Protocol.HTTP_2) + .message("") + .code(401) + .body("".toResponseBody()) + .build() + } + return Response.Builder().build() + } + + override fun isCanceled(): Boolean { + TODO("Not yet implemented") + } + + override fun isExecuted(): Boolean { + TODO("Not yet implemented") + } + + override fun request(): Request { + TODO("Not yet implemented") + } + + override fun timeout(): Timeout { + TODO("Not yet implemented") + } +} \ No newline at end of file -- GitLab From 9aab84956ac29a38116cd1444277c98239ed06ee Mon Sep 17 00:00:00 2001 From: Hasib Prince Date: Wed, 13 Sep 2023 14:52:14 +0600 Subject: [PATCH 2/2] added unit test for loginviewmodel --- .../apps/data/gplay/utils/GPlayHttpClient.kt | 2 + .../foundation/e/apps/NetworkHandlerTest.kt | 9 --- .../e/apps/{ => fused}/FusedApiImplTest.kt | 4 +- .../{ => fused}/FusedApiRepositoryTest.kt | 2 +- .../e/apps/{ => gplay}/GplyHttpClientTest.kt | 23 +++--- .../e/apps/login/LoginViewModelTest.kt | 70 +++++++++++++++++++ 6 files changed, 89 insertions(+), 21 deletions(-) delete mode 100644 app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt rename app/src/test/java/foundation/e/apps/{ => fused}/FusedApiImplTest.kt (99%) rename app/src/test/java/foundation/e/apps/{ => fused}/FusedApiRepositoryTest.kt (98%) rename app/src/test/java/foundation/e/apps/{ => gplay}/GplyHttpClientTest.kt (83%) create mode 100644 app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt diff --git a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt index 6c2929048..2bfd5578a 100644 --- a/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt +++ b/app/src/main/java/foundation/e/apps/data/gplay/utils/GPlayHttpClient.kt @@ -19,6 +19,7 @@ package foundation.e.apps.data.gplay.utils +import androidx.annotation.VisibleForTesting import com.aurora.gplayapi.data.models.PlayResponse import com.aurora.gplayapi.network.IHttpClient import foundation.e.apps.data.login.AuthObject @@ -61,6 +62,7 @@ class GPlayHttpClient @Inject constructor( const val STATUS_CODE_TIMEOUT = 408 } + @VisibleForTesting var okHttpClient = OkHttpClient().newBuilder() .retryOnConnectionFailure(false) .callTimeout(HTTP_TIMEOUT_IN_SECOND, TimeUnit.SECONDS) diff --git a/app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt b/app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt deleted file mode 100644 index 9912cb11a..000000000 --- a/app/src/test/java/foundation/e/apps/NetworkHandlerTest.kt +++ /dev/null @@ -1,9 +0,0 @@ -package foundation.e.apps - -import foundation.e.apps.data.handleNetworkResult - -class NetworkHandlerTest { - - fun testHandleNetworkResultWhenStatus401() { - } -} \ No newline at end of file diff --git a/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt b/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt similarity index 99% rename from app/src/test/java/foundation/e/apps/FusedApiImplTest.kt rename to app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt index c8e8e98aa..816394481 100644 --- a/app/src/test/java/foundation/e/apps/FusedApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/FusedApiImplTest.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package foundation.e.apps +package foundation.e.apps.fused import android.content.Context import android.text.format.Formatter @@ -25,6 +25,8 @@ import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.AuthData import com.aurora.gplayapi.data.models.Category import com.aurora.gplayapi.data.models.SearchBundle +import foundation.e.apps.FakePreferenceModule +import foundation.e.apps.R import foundation.e.apps.data.cleanapk.data.categories.Categories import foundation.e.apps.data.cleanapk.data.search.Search import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository diff --git a/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt b/app/src/test/java/foundation/e/apps/fused/FusedApiRepositoryTest.kt similarity index 98% rename from app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt rename to app/src/test/java/foundation/e/apps/fused/FusedApiRepositoryTest.kt index 8e04ce98e..801ae4437 100644 --- a/app/src/test/java/foundation/e/apps/FusedApiRepositoryTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/FusedApiRepositoryTest.kt @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -package foundation.e.apps +package foundation.e.apps.fused import foundation.e.apps.data.fused.FusedAPIRepository import foundation.e.apps.data.fused.FusedApiImpl diff --git a/app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt b/app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt similarity index 83% rename from app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt rename to app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt index 8593cffde..f906f6fb9 100644 --- a/app/src/test/java/foundation/e/apps/GplyHttpClientTest.kt +++ b/app/src/test/java/foundation/e/apps/gplay/GplyHttpClientTest.kt @@ -16,10 +16,11 @@ * along with this program. If not, see . */ -package foundation.e.apps +package foundation.e.apps.gplay import com.aurora.gplayapi.data.models.PlayResponse import foundation.e.apps.data.gplay.utils.GPlayHttpClient +import foundation.e.apps.data.login.AuthObject import foundation.e.apps.util.FakeCall import foundation.e.apps.util.MainCoroutineRule import foundation.e.apps.utils.SystemInfoProvider @@ -69,49 +70,49 @@ class GplyHttpClientTest { } @Test - fun testPostMapFailWhenStatus401() = runTest { + fun testPostWithMapFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.post("http://abc.abc", mapOf(), mapOf()) assertResponse(response) } @Test - fun testPostRequestBodyFailWhenStatus401() = runTest { + fun testPostWithRequestBodyFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.post("http://abc.abc", mapOf(), "".toRequestBody()) assertResponse(response) } @Test - fun testPostByteArrayRequestBodyFailWhenStatus401() = runTest { + fun testPostWithByteArrayFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.post("http://abc.abc", mapOf(), "".toByteArray()) assertResponse(response) } @Test - fun testGetFailWhenStatus401() = runTest { + fun testGetWithoutParamsFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.get(FakeCall.FAKE_URL, mapOf()) assertResponse(response) } @Test - fun testGetParamStringFailWhenStatus401() = runTest { + fun testGetWithStringParamsFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.get(FakeCall.FAKE_URL, mapOf(), "") assertResponse(response) } @Test - fun testGetMapFailWhenStatus401() = runTest { + fun testGetWithMapParamsFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.get(FakeCall.FAKE_URL, mapOf(), mapOf()) assertResponse(response) } @Test - fun testPostAuthFailWhenStatus401() = runTest { + fun testPostAuthFailedWhenStatus401() = runTest { initMocks() val response = gPlayHttpClient.postAuth("http://abc.abc", "".toByteArray()) assertResponse(response) @@ -126,7 +127,9 @@ class GplyHttpClientTest { private suspend fun assertResponse(response: PlayResponse) { assertFalse(response.isSuccessful) assertTrue(response.code == 401) - assertTrue(EventBus.events.first() is AppEvent.InvalidAuthEvent) + val event = EventBus.events.first() + assertTrue(event is AppEvent.InvalidAuthEvent) + assertTrue(event.data is String) + assertTrue(event.data == AuthObject.GPlayAuth::class.java.simpleName) } - } diff --git a/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt new file mode 100644 index 000000000..01521a1ff --- /dev/null +++ b/app/src/test/java/foundation/e/apps/login/LoginViewModelTest.kt @@ -0,0 +1,70 @@ +/* + * 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.login + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.data.ResultSupreme +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.data.login.LoginViewModel +import okhttp3.Cache +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +class LoginViewModelTest { + + @Mock + private lateinit var loginSourceRepository: LoginSourceRepository + @Mock + private lateinit var cache: Cache + + private lateinit var loginViewModel: LoginViewModel + + @Suppress("unused") + @get:Rule + val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule() + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + loginViewModel = LoginViewModel(loginSourceRepository, cache) + } + + @Test + fun testMarkInvalidAuthObject() { + val authObjectList = mutableListOf( + AuthObject.GPlayAuth( + ResultSupreme.Success(AuthData("aa@aa.com", "feri4234")), User.GOOGLE + ) + ) + loginViewModel.authObjects.value = authObjectList + + loginViewModel.markInvalidAuthObject(AuthObject.GPlayAuth::class.java.simpleName) + val currentAuthObjectList = loginViewModel.authObjects.value as List + val invalidGplayAuth = currentAuthObjectList.find { it is AuthObject.GPlayAuth } + + assert(invalidGplayAuth != null) + assert((invalidGplayAuth as AuthObject.GPlayAuth).result.isUnknownError()) + } +} \ No newline at end of file -- GitLab