diff --git a/.run/Run All Unit Tests.run.xml b/.run/Run All Unit Tests.run.xml
new file mode 100644
index 0000000..b9d29b3
--- /dev/null
+++ b/.run/Run All Unit Tests.run.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+
+
+
+ false
+ true
+
+
+
\ No newline at end of file
diff --git a/.run/Run Kover report.run.xml b/.run/Run Kover report.run.xml
new file mode 100644
index 0000000..48063ee
--- /dev/null
+++ b/.run/Run Kover report.run.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ true
+ false
+ false
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index a6b46fe..09f91d6 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -22,4 +22,5 @@ plugins {
alias(libs.plugins.android.library) apply false
alias(libs.plugins.kotlin.parcelize) apply false
alias(libs.plugins.maven.publish) apply false
+ alias(libs.plugins.kotlin.kover) apply false
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index d3abaad..096c8db 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -2,6 +2,7 @@
eudiRqesCore = "0.0.3-SNAPSHOT"
agp = "8.7.2"
kotlin = "2.0.21"
+kotlinxCoroutines = "1.8.1"
coreKtx = "1.15.0"
junit = "4.13.2"
junitVersion = "1.2.1"
@@ -20,6 +21,10 @@ koinAnnotations = "1.4.0"
ksp = "2.0.21-1.0.26"
gson = "2.11.0"
mavenPublish = "0.27.0"
+mockito = "5.12.0"
+mockitoKotlin = "5.3.1"
+kover = "0.7.5"
+robolectric = "4.11.1"
[libraries]
eudi-lib-android-rqes-core = { module = "eu.europa.ec.eudi:eudi-lib-android-rqes-core", version.ref = "eudiRqesCore" }
@@ -48,6 +53,10 @@ koin-test = { group = "io.insert-koin", name = "koin-android-test", version.ref
koin-annotations = { group = "io.insert-koin", name = "koin-annotations", version.ref = "koinAnnotations" }
koin-ksp = { group = "io.insert-koin", name = "koin-ksp-compiler", version.ref = "koinAnnotations" }
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
+mockito-core = { group = "org.mockito", name = "mockito-core", version.ref = "mockito" }
+mockito-kotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockitoKotlin" }
+kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" }
+robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
@@ -56,4 +65,5 @@ kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "ko
android-library = { id = "com.android.library", version.ref = "agp" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
-maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
\ No newline at end of file
+maven-publish = { id = "com.vanniktech.maven.publish", version.ref = "mavenPublish" }
+kotlin-kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
\ No newline at end of file
diff --git a/rqes-ui-sdk/build.gradle.kts b/rqes-ui-sdk/build.gradle.kts
index c64d5bb..adf92f2 100644
--- a/rqes-ui-sdk/build.gradle.kts
+++ b/rqes-ui-sdk/build.gradle.kts
@@ -23,6 +23,7 @@ plugins {
alias(libs.plugins.ksp)
alias(libs.plugins.kotlin.parcelize)
alias(libs.plugins.maven.publish)
+ alias(libs.plugins.kotlin.kover)
}
val NAMESPACE: String by project
@@ -103,6 +104,10 @@ dependencies {
// Test Dependencies
testImplementation(libs.junit)
testImplementation(libs.koin.test)
+ testImplementation(libs.mockito.core)
+ testImplementation(libs.mockito.kotlin)
+ testImplementation(libs.kotlinx.coroutines.test)
+ testImplementation(libs.robolectric)
}
// Compile time check
@@ -125,4 +130,28 @@ mavenPublishing {
url = "${POM_SCM_URL}/actions"
}
}
+}
+
+koverReport {
+ filters {
+ excludes {
+ packages(
+ "*.ksp.*",
+ "*.di",
+ "*.router",
+ "*.serializer",
+ "*.config",
+ "*.config.*",
+ "*.infrastructure.*",
+ "*.infrastructure",
+ "*.ui.component.*",
+ "*.ui.component",
+ "*.ui.container",
+ "*.ui.container.*",
+ )
+ classes(
+ "*Screen*",
+ )
+ }
+ }
}
\ No newline at end of file
diff --git a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSelectCertificateInteractor.kt b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSelectCertificateInteractor.kt
new file mode 100644
index 0000000..adc0464
--- /dev/null
+++ b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSelectCertificateInteractor.kt
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2023 European Commission
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
+ * except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/software/page/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the Licence for the specific language
+ * governing permissions and limitations under the Licence.
+ */
+
+package eu.europa.ec.eudi.rqesui.domain.interactor
+
+import eu.europa.ec.eudi.rqes.core.RQESService
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesAuthorizeServicePartialState
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesController
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesGetCertificatesPartialState
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesGetCredentialAuthorizationUrlPartialState
+import eu.europa.ec.eudi.rqesui.domain.entities.error.EudiRQESUiError
+import eu.europa.ec.eudi.rqesui.domain.extension.toUri
+import eu.europa.ec.eudi.rqesui.infrastructure.config.data.CertificateData
+import eu.europa.ec.eudi.rqesui.infrastructure.provider.ResourceProvider
+import eu.europa.ec.eudi.rqesui.util.CoroutineTestRule
+import eu.europa.ec.eudi.rqesui.util.mockedAuthorizationUrl
+import eu.europa.ec.eudi.rqesui.util.mockedExceptionWithMessage
+import eu.europa.ec.eudi.rqesui.util.mockedGenericErrorMessage
+import eu.europa.ec.eudi.rqesui.util.mockedPlainFailureMessage
+import eu.europa.ec.eudi.rqesui.util.runTest
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertTrue
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import org.robolectric.RobolectricTestRunner
+
+@RunWith(RobolectricTestRunner::class)
+class TestSelectCertificateInteractor {
+
+ @get:Rule
+ val coroutineRule = CoroutineTestRule()
+
+ @Mock
+ private lateinit var eudiController: EudiRqesController
+
+ @Mock
+ private lateinit var resourceProvider: ResourceProvider
+
+ @Mock
+ private lateinit var rqesServiceAuthorized: RQESService.Authorized
+
+ @Mock
+ private lateinit var certificateData: CertificateData
+
+ private lateinit var closeable: AutoCloseable
+
+ private lateinit var interactor: SelectCertificateInteractor
+
+ @Before
+ fun setUp() {
+ closeable = MockitoAnnotations.openMocks(this)
+ interactor = SelectCertificateInteractorImpl(resourceProvider, eudiController)
+ whenever(resourceProvider.genericErrorMessage())
+ .thenReturn(mockedGenericErrorMessage)
+ }
+
+ @After
+ fun after() {
+ closeable.close()
+ }
+
+ //region getSelectedFile
+ // Case 1:
+ // 1. Interactor's getSelectedFile is called.
+ // Case 1 Expected Result:
+ // 1. eudiController's getSelectedFile is called exactly once.
+ @Test
+ fun `Verify that getSelectedFile on the interactor calls getSelectedFile on the eudiController`() {
+ // When
+ interactor.getSelectedFile()
+
+ // Then
+ verify(eudiController, times(1))
+ .getSelectedFile()
+ }
+ //endregion
+
+ //region authorizeServiceAndFetchCertificates
+ // Case 1: Testing when both service authorization and fetching certificates are successful
+ // Case 1 Expected Result:
+ // 1. The interactor should return a success result that contains a list of certificates.
+ // 2. The returned result should be of type `SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Success`.
+ // 3. The `eudiController`'s `setAuthorizedService` method should be called once with `rqesServiceAuthorized`,
+ @Test
+ fun `Given Case 1, When authorizeServiceAndFetchCertificates is called, Then Case 1 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val listOfCertificates = listOf(certificateData)
+ mockAuthorizeServiceCall(
+ response = EudiRqesAuthorizeServicePartialState.Success(
+ authorizedService = rqesServiceAuthorized
+ )
+ )
+ mockGetAvailableCertificatesCall(
+ response = EudiRqesGetCertificatesPartialState.Success(
+ certificates = listOfCertificates
+ )
+ )
+
+ // Act
+ val result = interactor.authorizeServiceAndFetchCertificates()
+
+ // Assert
+ assertEquals(
+ SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Success(
+ certificates = listOfCertificates
+ ),
+ result
+ )
+ verify(eudiController, times(1))
+ .setAuthorizedService(rqesServiceAuthorized)
+ }
+
+ // Case 2: Testing when service authorization fails
+ // Case 2 Expected Result:
+ // 1. The interactor should return a failure result when the service authorization fails.
+ // 2. The returned result should be of type `SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure`.
+ // 3. The failure should contain the mocked error from the service authorization failure.
+ // 4. The eudiController's `authorizeService` method should be called exactly once
+ @Test
+ fun `Given Case 2, When authorizeServiceAndFetchCertificates is called, Then Case 2 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val mockError = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockAuthorizeServiceCall(
+ response = EudiRqesAuthorizeServicePartialState.Failure(
+ error = mockError
+ )
+ )
+
+ // Act
+ val result = interactor.authorizeServiceAndFetchCertificates()
+
+ // Assert
+ assertEquals(
+ SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure(
+ error = mockError
+ ),
+ result
+ )
+ verify(eudiController, times(1))
+ .authorizeService()
+ }
+
+ // Case 3: Testing when an exception is thrown during the service authorization process
+ // Case 3 Expected Result:
+ // 1. The interactor should return a failure result when the service authorization throws an exception.
+ // 2. The returned result should be of type `SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure`.
+ // 3. The failure should contain the thrown exception.
+ // 4. The error message in the returned failure result should match the message of the thrown exception.
+ @Test
+ fun `Given Case 3, When authorizeServiceAndFetchCertificates is called, Then Case 3 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ whenever(eudiController.authorizeService())
+ .thenThrow(mockedExceptionWithMessage)
+
+ // Act
+ val result = interactor.authorizeServiceAndFetchCertificates()
+
+ // Assert
+ assertTrue(result is SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure)
+ assertEquals(
+ mockedExceptionWithMessage.message,
+ (result as SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure).error.message
+ )
+ }
+
+ // Case 4: Testing when fetching certificates fails
+ // Case 4 Expected Result:
+ // 1. The interactor should return a failure result when fetching certificates fails.
+ // 2. The returned result should be of type `SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure`.
+ // 3. The failure should contain the error from `EudiRqesGetCertificatesPartialState.Failure`.
+ @Test
+ fun `Given Case 4, When authorizeServiceAndFetchCertificates is called, Then Case 4 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val mockError = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockAuthorizeServiceCall(
+ response = EudiRqesAuthorizeServicePartialState.Success(
+ authorizedService = rqesServiceAuthorized
+ )
+ )
+ mockGetAvailableCertificatesCall(
+ response = EudiRqesGetCertificatesPartialState.Failure(
+ error = mockError
+ )
+ )
+
+ // Act
+ val result = interactor.authorizeServiceAndFetchCertificates()
+
+ // Assert
+ assertEquals(
+ SelectCertificateInteractorAuthorizeServiceAndFetchCertificatesPartialState.Failure(
+ error = mockError
+ ),
+ result
+ )
+ }
+ //endregion
+
+ //region getCredentialAuthorizationUrl
+ // Case 1: Testing when getCredentialAuthorizationUrl successfully returns an authorization URL
+ // Case 1 Expected Result:
+ // 1. The interactor should call getCredentialAuthorizationUrl and return a success response containing the authorization URL.
+ // 2. The returned result should be of type `EudiRqesGetCredentialAuthorizationUrlPartialState.Success`.
+ // 3. The authorization Uri returned should match the mocked `mockedAuthorizationUrl` converted to Uri.
+ @Test
+ fun `Given Case 1, When getCredentialAuthorizationUrl is called, Then Case 1 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val successResponse =
+ EudiRqesGetCredentialAuthorizationUrlPartialState.Success(
+ authorizationUrl = mockedAuthorizationUrl.toUri()
+ )
+
+ whenever(eudiController.getAuthorizedService())
+ .thenReturn(rqesServiceAuthorized)
+ mockGetCredentialAuthorizationUrlCall(response = successResponse)
+
+ // Act
+ val result = interactor.getCredentialAuthorizationUrl(certificate = certificateData)
+
+ // Assert
+ assertEquals(successResponse, result)
+ }
+
+ // Case 2: Testing when getCredentialAuthorizationUrl fails due to an error
+ // Case 2 Expected Result:
+ // 1. The interactor should call getCredentialAuthorizationUrl and return a failure response with a `mockedPlainFailureMessage`.
+ // 2. The returned result should be of type `EudiRqesGetCredentialAuthorizationUrlPartialState.Failure`.
+ // 3. The error in the returned result should match the `mockFailureError` passed in the failure response.
+ @Test
+ fun `Given Case 2, When getCredentialAuthorizationUrl is called, Then Case 2 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val failureError = EudiRQESUiError(
+ message = mockedPlainFailureMessage
+ )
+ val failureResponse =
+ EudiRqesGetCredentialAuthorizationUrlPartialState.Failure(error = failureError)
+
+ whenever(eudiController.getAuthorizedService())
+ .thenReturn(rqesServiceAuthorized)
+ mockGetCredentialAuthorizationUrlCall(
+ response = failureResponse
+ )
+
+ // Act
+ val result = interactor.getCredentialAuthorizationUrl(certificate = certificateData)
+
+ // Assert
+ assertTrue(result is EudiRqesGetCredentialAuthorizationUrlPartialState.Failure)
+ assertEquals(
+ failureError,
+ (result as EudiRqesGetCredentialAuthorizationUrlPartialState.Failure).error
+ )
+ }
+
+ // Case 3: Testing when getCredentialAuthorizationUrl throws an exception
+ // Case 3 Expected Result:
+ // 1. The interactor should call getCredentialAuthorizationUrl and handle the exception thrown.
+ // 2. The returned result should be of type `EudiRqesGetCredentialAuthorizationUrlPartialState.Failure`.
+ // 3. The error message in the returned result should match the message of the thrown exception.
+ @Test
+ fun `Given Case 3, When getCredentialAuthorizationUrl is called, Then Case 3 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ whenever(eudiController.getAuthorizedService())
+ .thenReturn(rqesServiceAuthorized)
+ whenever(
+ eudiController.getCredentialAuthorizationUrl(
+ authorizedService = rqesServiceAuthorized,
+ certificateData = certificateData
+ )
+ ).thenThrow(mockedExceptionWithMessage)
+
+ // Act
+ val result = interactor.getCredentialAuthorizationUrl(certificate = certificateData)
+
+ // Assert
+ assertTrue(result is EudiRqesGetCredentialAuthorizationUrlPartialState.Failure)
+ assertEquals(
+ mockedExceptionWithMessage.message,
+ (result as EudiRqesGetCredentialAuthorizationUrlPartialState.Failure).error.message
+ )
+ }
+
+ // Case 4:
+ // Testing when the `getCredentialAuthorizationUrl` method is called, and the `getAuthorizedService`
+ // returns `null`. This simulates a case where no authorized service is available, triggering a fallback
+ // to a generic error message.
+ // Case 4 Expected Result:
+ // 1. The interactor should return a failure response of type `EudiRqesGetCredentialAuthorizationUrlPartialState.Failure`.
+ // 2. The error message in the failure response should match the `mockedGenericErrorMessage`.
+ @Test
+ fun `Given Case 4, When getCredentialAuthorizationUrl is called, Then Case 4 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ whenever(eudiController.getAuthorizedService())
+ .thenReturn(null)
+
+ // Act
+ val result = interactor.getCredentialAuthorizationUrl(certificate = certificateData)
+
+ // Assert
+ assertTrue(result is EudiRqesGetCredentialAuthorizationUrlPartialState.Failure)
+ assertEquals(
+ mockedGenericErrorMessage,
+ (result as EudiRqesGetCredentialAuthorizationUrlPartialState.Failure).error.message
+ )
+ }
+
+ // Case 5 Description:
+ // Testing when the `getCredentialAuthorizationUrl` method is called and the `getAuthorizedService`
+ // returns a valid service but the `getCredentialAuthorizationUrl` method itself returns `null`.
+ // This simulates a case where the service does not provide a valid authorization URL.
+ // Case 5:Expected Result:
+ // 1. The interactor should return a failure response of type `EudiRqesGetCredentialAuthorizationUrlPartialState.Failure`.
+ // 2. The error message in the failure response should match the `mockedGenericErrorMessage`.
+ @Test
+ fun `Given Case 5, When getCredentialAuthorizationUrl is called, Then Case 5 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ whenever(eudiController.getAuthorizedService())
+ .thenReturn(rqesServiceAuthorized)
+ whenever(
+ eudiController.getCredentialAuthorizationUrl(
+ authorizedService = rqesServiceAuthorized,
+ certificateData = certificateData
+ )
+ ).thenReturn(null)
+
+ // Act
+ val result = interactor.getCredentialAuthorizationUrl(certificate = certificateData)
+
+ // Assert
+ assertTrue(result is EudiRqesGetCredentialAuthorizationUrlPartialState.Failure)
+ assertEquals(
+ mockedGenericErrorMessage,
+ (result as EudiRqesGetCredentialAuthorizationUrlPartialState.Failure).error.message
+ )
+ }
+ //endregion getCredentialAuthorizationUrl
+
+ //region helper functions
+ private suspend fun mockAuthorizeServiceCall(response: EudiRqesAuthorizeServicePartialState) {
+ whenever(eudiController.authorizeService()).thenReturn(response)
+ }
+
+ private suspend fun mockGetAvailableCertificatesCall(response: EudiRqesGetCertificatesPartialState) {
+ whenever(eudiController.getAvailableCertificates(rqesServiceAuthorized)).thenReturn(response)
+ }
+
+ private suspend fun mockGetCredentialAuthorizationUrlCall(response: EudiRqesGetCredentialAuthorizationUrlPartialState) {
+ whenever(
+ eudiController.getCredentialAuthorizationUrl(
+ authorizedService = rqesServiceAuthorized,
+ certificateData = certificateData
+ )
+ ).thenReturn(response)
+ }
+ //endregion
+}
+
diff --git a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSelectQtspInteractor.kt b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSelectQtspInteractor.kt
new file mode 100644
index 0000000..eee07e1
--- /dev/null
+++ b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSelectQtspInteractor.kt
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2023 European Commission
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
+ * except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/software/page/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the Licence for the specific language
+ * governing permissions and limitations under the Licence.
+ */
+
+package eu.europa.ec.eudi.rqesui.domain.interactor
+
+import eu.europa.ec.eudi.rqes.core.RQESService
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesController
+import eu.europa.ec.eudi.rqesui.infrastructure.config.data.QtspData
+import kotlinx.coroutines.test.runTest
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.times
+import org.mockito.kotlin.verify
+
+class TestSelectQtspInteractor {
+
+ @Mock
+ private lateinit var eudiController: EudiRqesController
+
+ @Mock
+ private lateinit var qtspData: QtspData
+
+ @Mock
+ private lateinit var rqesService: RQESService
+
+ private lateinit var closeable: AutoCloseable
+
+ private lateinit var interactor: SelectQtspInteractor
+
+ @Before
+ fun setUp() {
+ closeable = MockitoAnnotations.openMocks(this)
+ interactor = SelectQtspInteractorImpl(eudiController)
+ }
+
+ @After
+ fun after() {
+ closeable.close()
+ }
+
+ //region getQtsps
+ // Case 1:
+ // 1. Interactor's getQtsps is called.
+ // Case 1 Expected Result:
+ // 1. eudiController's getQtsps is called exactly once.
+ @Test
+ fun `Verify that When getQtsps is called, Then getQtsps is executed on the eudiController`() {
+ // When
+ interactor.getQtsps()
+
+ // Then
+ verify(eudiController, times(1))
+ .getQtsps()
+ }
+ //endregion
+
+ //region getSelectedFile
+ // Case 1:
+ // 1. Interactor's getSelectedFile is called.
+ // Case 1 Expected Result:
+ // 1. eudiController's getSelectedFile is called exactly once.
+ @Test
+ fun `Verify that When getSelectedFile is called, Then getSelectedFile is executed on the eudiController`() {
+ // When
+ interactor.getSelectedFile()
+
+ // Then
+ verify(eudiController, times(1))
+ .getSelectedFile()
+ }
+ //endregion
+
+ //region updateQtspUserSelection
+ // Case 1:
+ // 1. Interactor's updateQtspUserSelection is called.
+ // Case 1 Expected Result:
+ // 1. eudiController's setSelectedQtsp is called exactly once.
+ @Test
+ fun `Verify that When updateQtspUserSelection is called, Then setSelectedQtsp is executed on the eudiController`() {
+ interactor.updateQtspUserSelection(qtspData)
+
+ verify(eudiController, times(1))
+ .setSelectedQtsp(qtspData)
+ }
+ //endregion
+
+ //region getServiceAuthorizationUrl
+ // Case 1:
+ // 1. Interactor's getServiceAuthorizationUrl is called.
+ // Case 1 Expected Result:
+ // 1. eudiController's getServiceAuthorizationUrl is called exactly once.
+ @Test
+ fun `Verify that When getServiceAuthorizationUrl is called, Then getServiceAuthorizationUrl is executed on the eudiController`() =
+ runTest {
+ interactor.getServiceAuthorizationUrl(rqesService)
+
+ verify(eudiController, times(1))
+ .getServiceAuthorizationUrl(rqesService)
+ }
+ //endregion
+}
\ No newline at end of file
diff --git a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSuccessInteractor.kt b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSuccessInteractor.kt
new file mode 100644
index 0000000..77b33c6
--- /dev/null
+++ b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/domain/interactor/TestSuccessInteractor.kt
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2023 European Commission
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
+ * except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/software/page/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the Licence for the specific language
+ * governing permissions and limitations under the Licence.
+ */
+
+package eu.europa.ec.eudi.rqesui.domain.interactor
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.Cursor
+import android.net.Uri
+import android.provider.OpenableColumns
+import eu.europa.ec.eudi.rqes.core.RQESService
+import eu.europa.ec.eudi.rqes.core.SignedDocuments
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesAuthorizeCredentialPartialState
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesController
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesGetSelectedFilePartialState
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesGetSelectedQtspPartialState
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesSaveSignedDocumentsPartialState
+import eu.europa.ec.eudi.rqesui.domain.controller.EudiRqesSignDocumentsPartialState
+import eu.europa.ec.eudi.rqesui.domain.entities.error.EudiRQESUiError
+import eu.europa.ec.eudi.rqesui.infrastructure.config.data.DocumentData
+import eu.europa.ec.eudi.rqesui.infrastructure.config.data.QtspData
+import eu.europa.ec.eudi.rqesui.infrastructure.provider.ResourceProvider
+import eu.europa.ec.eudi.rqesui.util.CoroutineTestRule
+import eu.europa.ec.eudi.rqesui.util.mockedDocumentName
+import eu.europa.ec.eudi.rqesui.util.mockedExceptionWithMessage
+import eu.europa.ec.eudi.rqesui.util.mockedExceptionWithNoMessage
+import eu.europa.ec.eudi.rqesui.util.mockedGenericErrorMessage
+import eu.europa.ec.eudi.rqesui.util.mockedPlainFailureMessage
+import eu.europa.ec.eudi.rqesui.util.runTest
+import junit.framework.TestCase.assertEquals
+import junit.framework.TestCase.assertTrue
+import org.junit.After
+import org.junit.Before
+import org.junit.Rule
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.whenever
+import org.robolectric.RobolectricTestRunner
+import kotlin.test.Test
+
+@RunWith(RobolectricTestRunner::class)
+class TestSuccessInteractor {
+
+ @get:Rule
+ val coroutineRule = CoroutineTestRule()
+
+ private lateinit var interactor: SuccessInteractor
+
+ @Mock
+ private lateinit var resourceProvider: ResourceProvider
+
+ @Mock
+ private lateinit var eudiRqesController: EudiRqesController
+
+ @Mock
+ private lateinit var credentialAuthorized: RQESService.CredentialAuthorized
+
+ @Mock
+ private lateinit var signedDocuments: SignedDocuments
+
+ @Mock
+ private lateinit var qtspData: QtspData
+
+ @Mock
+ private lateinit var documentData: DocumentData
+
+ @Mock
+ private lateinit var context: Context
+
+ @Mock
+ private lateinit var contentResolver: ContentResolver
+
+ @Mock
+ private lateinit var cursor: Cursor
+
+ private lateinit var closeable: AutoCloseable
+
+ private val documentFileUri = Uri.parse("content://example.provider/documents/document.pdf")
+
+ @Before
+ fun setUp() {
+ closeable = MockitoAnnotations.openMocks(this)
+ interactor = SuccessInteractorImpl(resourceProvider, eudiRqesController)
+ whenever(resourceProvider.genericErrorMessage())
+ .thenReturn(mockedGenericErrorMessage)
+ whenever(resourceProvider.provideContext()).thenReturn(context)
+ }
+
+ @After
+ fun after() {
+ closeable.close()
+ }
+
+ //region getSelectedFileAndQtsp
+ // Case 1: Testing when `getSelectedFileAndQtsp` successfully returns a file and Qtsp
+ // Case 1 Expected Result:
+ // 1. The interactor should call `getSelectedFileAndQtsp` and successfully retrieve both the file and the Qtsp data.
+ // 2. The returned result should be of type `SuccessInteractorGetSelectedFileAndQtspPartialState.Success`.
+ // 3. The `selectedFile` in the returned result should match the mock `documentData`.
+ // 4. The `selectedQtsp` in the returned result should match the mock `qtspData`.
+ @Test
+ fun `Given Case 1, When getSelectedFileAndQtsp is called, Then Case 1 expected result is returned`() {
+ // Arrange
+ mockGetSelectedFileCall(event = EudiRqesGetSelectedFilePartialState.Success(file = documentData))
+ mockGetSelectedQtspCall(event = EudiRqesGetSelectedQtspPartialState.Success(qtsp = qtspData))
+
+ // Act
+ val result = interactor.getSelectedFileAndQtsp()
+
+ // Assert
+ assertTrue(result is SuccessInteractorGetSelectedFileAndQtspPartialState.Success)
+ val success = result as SuccessInteractorGetSelectedFileAndQtspPartialState.Success
+ assertEquals(documentData, success.selectedFile)
+ assertEquals(qtspData, success.selectedQtsp)
+ }
+
+ // Case 2: Testing when `getSelectedFileAndQtsp` fails to retrieve the file and Qtsp
+ // Case 2 Expected Result:
+ // 1. The interactor should call `getSelectedFileAndQtsp` and handle a failure when retrieving the file.
+ // 2. The returned result should be of type `Failure` from `SuccessInteractorGetSelectedFileAndQtspPartialState`.
+ // 3. The `error` in the returned result should match the mocked `EudiRQESUiError` with the failure message.
+ @Test
+ fun `Given Case 2, When getSelectedFileAndQtsp is called, Then Case 2 expected result is returned`() {
+ // Arrange
+ val error = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockGetSelectedFileCall(event = EudiRqesGetSelectedFilePartialState.Failure(error = error))
+
+ // Act
+ val result = interactor.getSelectedFileAndQtsp()
+
+ // Assert
+ assertTrue(result is SuccessInteractorGetSelectedFileAndQtspPartialState.Failure)
+ assertEquals(
+ error,
+ (result as SuccessInteractorGetSelectedFileAndQtspPartialState.Failure).error
+ )
+ }
+
+ // Case 3: Testing when `getSelectedFileAndQtsp` successfully retrieves the file,
+ // but fails to retrieve the Qtsp.
+ // Case 3 Expected Result:
+ // 1. The interactor should successfully retrieve the file.
+ // 2. The interactor should fail when retrieving the Qtsp and handle the error.
+ // 3. The returned result should be of type Failure of `SuccessInteractorGetSelectedFileAndQtspPartialState`.
+ // 4. The `error` in the returned result should match the mocked `EudiRQESUiError` with the failure message.
+ @Test
+ fun `Given Case 3, When getSelectedFileAndQtsp is called, Then Case 3 expected result is returned`() {
+ // Arrange
+ val error = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockGetSelectedFileCall(event = EudiRqesGetSelectedFilePartialState.Success(file = documentData))
+ mockGetSelectedQtspCall(event = EudiRqesGetSelectedQtspPartialState.Failure(error = error))
+
+ // Act
+ val result = interactor.getSelectedFileAndQtsp()
+
+ // Assert
+ assertTrue(result is SuccessInteractorGetSelectedFileAndQtspPartialState.Failure)
+ assertEquals(
+ error,
+ (result as SuccessInteractorGetSelectedFileAndQtspPartialState.Failure).error
+ )
+ }
+
+ // Case 4:
+ // This test case ensures that the `getOrElse` fallback logic is executed when an exception
+ // is thrown during the `getSelectedFile` call
+ // Expected Result:
+ // 1. The exception is caught and processed within the `getOrElse` block.
+ // 2. The result should be a `Failure` with the exception's message included in the error.
+ @Test
+ fun `Given Case 4, When getSelectedFileAndQtsp is called, Then Case 4 expected result is returned`() {
+ // Arrange
+ whenever(eudiRqesController.getSelectedFile())
+ .thenThrow(mockedExceptionWithMessage)
+
+ // Act
+ val result = interactor.getSelectedFileAndQtsp()
+
+ // Assert
+ assertTrue(result is SuccessInteractorGetSelectedFileAndQtspPartialState.Failure)
+ val failure = result as SuccessInteractorGetSelectedFileAndQtspPartialState.Failure
+ assertEquals(mockedExceptionWithMessage.message, failure.error.message)
+ }
+
+ // Case 5:
+ // This test case ensures that the `getOrElse` fallback logic is executed when an exception
+ // without a message is thrown during the `getSelectedFile` call. It verifies that the
+ // returned `Failure` state contains the generic error message as the error.
+ // Expected Result:
+ // 1. The exception is caught and processed within the `getOrElse` block.
+ // 2. The result should be a `Failure` with the mocked generic error message.
+ @Test
+ fun `Given Case 5, When getSelectedFileAndQtsp is called, Then Case 5 expected result is returned`() {
+ // Arrange
+ whenever(eudiRqesController.getSelectedFile())
+ .thenThrow(mockedExceptionWithNoMessage)
+
+ // Act
+ val result = interactor.getSelectedFileAndQtsp()
+
+ // Assert
+ assertTrue(result is SuccessInteractorGetSelectedFileAndQtspPartialState.Failure)
+ val failure = result as SuccessInteractorGetSelectedFileAndQtspPartialState.Failure
+ assertEquals(mockedGenericErrorMessage, failure.error.message)
+ }
+ //endregion
+
+ //region signAndSaveDocument
+ // Case 1 Description:
+ // This test verifies the `signAndSaveDocument` function under a success scenario.
+ // The method is expected to successfully execute the following steps:
+ // 1. Authorize a credential.
+ // 2. Sign the document(s) using the authorized credential.
+ // 3. Save the signed document(s) to the provided location.
+ // Case 1 Expected Result:
+ // The function should return a `SuccessInteractorSignAndSaveDocumentPartialState.Success` state
+ // with the correct saved document data, and all operations should complete successfully.
+ @Test
+ fun `Given Case 1, When signAndSaveDocument is called, Then Case 1 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val documentsUri = listOf(documentFileUri)
+ mockAuthorizeCredentialCall(
+ response = EudiRqesAuthorizeCredentialPartialState.Success(
+ authorizedCredential = credentialAuthorized
+ )
+ )
+ mockSignDocumentsCall(
+ response = EudiRqesSignDocumentsPartialState.Success(
+ signedDocuments = signedDocuments
+ )
+ )
+ mockSaveSignedDocumentsCall(
+ documentName = mockedDocumentName,
+ signedDocuments = signedDocuments,
+ event = EudiRqesSaveSignedDocumentsPartialState.Success(savedDocumentsUri = documentsUri)
+ )
+ mockGetFileNameFromUri()
+
+ // Act
+ val result = interactor.signAndSaveDocument(mockedDocumentName)
+
+ // Assert
+ assertTrue(result is SuccessInteractorSignAndSaveDocumentPartialState.Success)
+ }
+
+ // Case 2: Testing when `signAndSaveDocument` fails during the credential authorization step.
+ // Case 2 Expected Result:
+ // 1. The interactor should attempt to authorize the credential via `authorizeCredential`.
+ // 2. The authorization should fail and return an error.
+ // 3. The returned result from `signAndSaveDocument` should be of type `Failure` from `SuccessInteractorSignAndSaveDocumentPartialState`.
+ // 4. The `error` in the returned result should match the mocked error.
+ @Test
+ fun `Given Case 2, When signAndSaveDocument is called, Then Case 2 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val error = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockAuthorizeCredentialCall(
+ response = EudiRqesAuthorizeCredentialPartialState.Failure(
+ error = error
+ )
+ )
+
+ // Act
+ val result = interactor.signAndSaveDocument(mockedDocumentName)
+
+ // Assert
+ assertTrue(result is SuccessInteractorSignAndSaveDocumentPartialState.Failure)
+ assertEquals(
+ error,
+ (result as SuccessInteractorSignAndSaveDocumentPartialState.Failure).error
+ )
+ }
+
+ // Case 3: Testing when `signAndSaveDocument` succeeds in credential authorization but fails during document signing.
+ // Case 3 Expected Result:
+ // 1. The interactor should first authorize the credential successfully via `authorizeCredential`.
+ // 2. After authorization, the document signing should be attempted, but it fails.
+ // 3. The failure should return an error.
+ // 4. The returned result from `signAndSaveDocument` should be of type `Failure` of `SuccessInteractorSignAndSaveDocumentPartialState`.
+ // 5. The error in the returned result should match the mocked error.
+ @Test
+ fun `Given Case 3, When signAndSaveDocument is called, Then Case 3 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val error = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockAuthorizeCredentialCall(
+ response = EudiRqesAuthorizeCredentialPartialState.Success(
+ credentialAuthorized
+ )
+ )
+ mockSignDocumentsCall(response = EudiRqesSignDocumentsPartialState.Failure(error = error))
+
+ // Act
+ val result = interactor.signAndSaveDocument(mockedDocumentName)
+
+ // Assert
+ assertTrue(result is SuccessInteractorSignAndSaveDocumentPartialState.Failure)
+ assertEquals(
+ error,
+ (result as SuccessInteractorSignAndSaveDocumentPartialState.Failure).error
+ )
+ }
+
+ // Case 4: Testing when `signAndSaveDocument` successfully goes ahead with an authorized credential,
+ // signs the document but fails to save the signed document.
+ // Case 4 Expected Result:
+ // 1. The interactor should first successfully continue with an authorized credential.
+ // 2. After successful authorization, the document should be signed successfully.
+ // 3. After signing, the interactor attempts to save the signed document, but this fails.
+ // 4. The failure during the save operation should return an error (`EudiRQESUiError`).
+ // 5. The result from `signAndSaveDocument` should be of type `Failure` of `SuccessInteractorSignAndSaveDocumentPartialState`.
+ // 6. The `error` in the returned result should match the mocked error.
+ @Test
+ fun `Given Case 4, When signAndSaveDocument is called, Then Case 4 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ val error = EudiRQESUiError(message = mockedPlainFailureMessage)
+ mockAuthorizeCredentialCall(
+ response = EudiRqesAuthorizeCredentialPartialState.Success(
+ authorizedCredential = credentialAuthorized
+ )
+ )
+ mockSignDocumentsCall(
+ response = EudiRqesSignDocumentsPartialState.Success(
+ signedDocuments = signedDocuments
+ )
+ )
+ mockSaveSignedDocumentsCall(
+ documentName = mockedDocumentName,
+ signedDocuments = signedDocuments,
+ event = EudiRqesSaveSignedDocumentsPartialState.Failure(error = error)
+ )
+
+ // Act
+ val result = interactor.signAndSaveDocument(mockedDocumentName)
+
+ // Assert
+ assertTrue(result is SuccessInteractorSignAndSaveDocumentPartialState.Failure)
+ assertEquals(
+ error,
+ (result as SuccessInteractorSignAndSaveDocumentPartialState.Failure).error
+ )
+ }
+
+ // Case 5:
+ // This test verifies the `signAndSaveDocument` function under a failure scenario.
+ // The method is expected to handle an exception thrown by the `authorizeCredential` call gracefully.
+ // Case 5 Expected Result:
+ // The function should return a `SuccessInteractorSignAndSaveDocumentPartialState.Failure` state
+ // with an error message matching the mocked exception's message.
+ @Test
+ fun `Given Case 5, When signAndSaveDocument is called, Then Case 5 expected result is returned`() =
+ coroutineRule.runTest {
+ // Arrange
+ whenever(eudiRqesController.authorizeCredential()).thenThrow(
+ mockedExceptionWithMessage
+ )
+
+ // Act
+ val result = interactor.signAndSaveDocument(mockedDocumentName)
+
+ // Assert
+ assertTrue(result is SuccessInteractorSignAndSaveDocumentPartialState.Failure)
+ val failureState = result as SuccessInteractorSignAndSaveDocumentPartialState.Failure
+ assertTrue(failureState.error.message == mockedExceptionWithMessage.message)
+ }
+ //endregion
+
+ //region helper functions
+ private fun mockGetSelectedFileCall(event: EudiRqesGetSelectedFilePartialState) {
+ whenever(eudiRqesController.getSelectedFile()).thenReturn(event)
+ }
+
+ private fun mockGetSelectedQtspCall(event: EudiRqesGetSelectedQtspPartialState) {
+ whenever(eudiRqesController.getSelectedQtsp()).thenReturn(event)
+ }
+
+ private suspend fun mockAuthorizeCredentialCall(response: EudiRqesAuthorizeCredentialPartialState) {
+ whenever(eudiRqesController.authorizeCredential()).thenReturn(
+ response
+ )
+ }
+
+ private suspend fun mockSignDocumentsCall(response: EudiRqesSignDocumentsPartialState) {
+ whenever(eudiRqesController.signDocuments(credentialAuthorized))
+ .thenReturn(response)
+ }
+
+ private suspend fun mockSaveSignedDocumentsCall(
+ documentName: String,
+ signedDocuments: SignedDocuments,
+ event: EudiRqesSaveSignedDocumentsPartialState
+ ) {
+ whenever(
+ eudiRqesController.saveSignedDocuments(
+ originalDocumentName = documentName,
+ signedDocuments = signedDocuments
+ )
+ ).thenReturn(event)
+ }
+
+ private fun mockGetFileNameFromUri() {
+ whenever(context.contentResolver).thenReturn(contentResolver)
+ whenever(
+ contentResolver.query(documentFileUri, null, null, null, null)
+ ).thenReturn(cursor)
+ whenever(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)).thenReturn(0)
+ whenever(cursor.moveToFirst()).thenReturn(true)
+ whenever(cursor.getString(0)).thenReturn(mockedDocumentName)
+ }
+ //endregion
+}
+
+
+
+
+
diff --git a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/presentation/extension/TestUriExtensionsKt.kt b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/presentation/extension/TestUriExtensionsKt.kt
new file mode 100644
index 0000000..b0e6169
--- /dev/null
+++ b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/presentation/extension/TestUriExtensionsKt.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 2023 European Commission
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
+ * except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/software/page/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the Licence for the specific language
+ * governing permissions and limitations under the Licence.
+ */
+
+package eu.europa.ec.eudi.rqesui.presentation.extension
+
+import android.content.ContentResolver
+import android.content.Context
+import android.database.Cursor
+import android.net.Uri
+import android.provider.OpenableColumns
+import eu.europa.ec.eudi.rqesui.util.mockedDocumentName
+import junit.framework.TestCase.assertEquals
+import org.junit.After
+import org.junit.Before
+import org.mockito.Mock
+import org.mockito.MockitoAnnotations
+import org.mockito.kotlin.verify
+import org.mockito.kotlin.whenever
+import kotlin.test.Test
+
+class TestUriExtensionsKt {
+
+ @Mock
+ private lateinit var fileUri: Uri
+
+ @Mock
+ private lateinit var context: Context
+
+ @Mock
+ private lateinit var contentResolver: ContentResolver
+
+ @Mock
+ private lateinit var cursor: Cursor
+
+ private lateinit var closeable: AutoCloseable
+
+ @Before
+ fun setUp() {
+ closeable = MockitoAnnotations.openMocks(this)
+ }
+
+ @After
+ fun after() {
+ closeable.close()
+ }
+
+ //region getFileName
+ @Test
+ fun `When getFileName is called, Then assert the expected file name is returned`() {
+ whenever(context.contentResolver).thenReturn(contentResolver)
+ whenever(
+ contentResolver.query(
+ fileUri, null, null, null, null
+ )
+ ).thenReturn(cursor)
+
+ whenever(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME)).thenReturn(0)
+ whenever(cursor.moveToFirst()).thenReturn(true)
+ whenever(cursor.getString(0)).thenReturn(mockedDocumentName)
+
+ val fileNameResult = fileUri.getFileName(context).getOrThrow()
+
+ assertEquals(mockedDocumentName, fileNameResult)
+ verify(cursor).close()
+ }
+ //endregion
+}
\ No newline at end of file
diff --git a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/ExampleUnitTest.kt b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/util/Constants.kt
similarity index 61%
rename from rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/ExampleUnitTest.kt
rename to rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/util/Constants.kt
index 24abae7..edafae2 100644
--- a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/ExampleUnitTest.kt
+++ b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/util/Constants.kt
@@ -14,19 +14,12 @@
* governing permissions and limitations under the Licence.
*/
-package eu.europa.ec.eudi.rqesui
+package eu.europa.ec.eudi.rqesui.util
-import org.junit.Assert.assertEquals
-import org.junit.Test
+const val mockedPlainFailureMessage = "Failure message"
+const val mockedGenericErrorMessage = "resourceProvider's genericErrorMessage"
+const val mockedAuthorizationUrl = "https://endpoint.com/mockedAuthorizationUrl"
+const val mockedDocumentName = "Document.pdf"
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * See [testing documentation](http://d.android.com/tools/testing).
- */
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file
+val mockedExceptionWithMessage = RuntimeException("Exception to test interactor.")
+val mockedExceptionWithNoMessage = RuntimeException()
\ No newline at end of file
diff --git a/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/util/CoroutineTestRule.kt b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/util/CoroutineTestRule.kt
new file mode 100644
index 0000000..3cddbf0
--- /dev/null
+++ b/rqes-ui-sdk/src/test/java/eu/europa/ec/eudi/rqesui/util/CoroutineTestRule.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2023 European Commission
+ *
+ * Licensed under the EUPL, Version 1.2 or - as soon they will be approved by the European
+ * Commission - subsequent versions of the EUPL (the "Licence"); You may not use this work
+ * except in compliance with the Licence.
+ *
+ * You may obtain a copy of the Licence at:
+ * https://joinup.ec.europa.eu/software/page/eupl
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under
+ * the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF
+ * ANY KIND, either express or implied. See the Licence for the specific language
+ * governing permissions and limitations under the Licence.
+ */
+
+package eu.europa.ec.eudi.rqesui.util
+
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.TestDispatcher
+import kotlinx.coroutines.test.TestScope
+import kotlinx.coroutines.test.runTest
+import org.junit.rules.TestWatcher
+
+class CoroutineTestRule(
+ private val testDispatcher: TestDispatcher = StandardTestDispatcher(),
+ val testScope: TestScope = TestScope(testDispatcher)
+) : TestWatcher()
+
+fun CoroutineTestRule.runTest(block: suspend CoroutineScope.() -> Unit): Unit =
+ testScope.runTest { block() }
\ No newline at end of file